目录
6.3、vue2判断数据是否变化是通过hasChanged函数实现的
①为什么在x === y成立的时候,还要做一个return x === 0 && 1 / x !== 1 / (y as number)的判断?
②为什么在 x === y 不成立的时候,还要做一个x === x || y === y的判断?
Vue优化路径
一、使用key
也就是使用v-for这样通过循环生成的列表,应给每个列表项一个稳定且唯一的key,这样有利于在列表变动时,尽量少的删除、新增、改动元素。
二、使用冻结对象
在不需要改动的数据时(比如只读的数据:面向顾客的商品列表等),将对象冻结。例如:
var obj = { a : 1, b : 2}
Object.freeze(obj)
obj.a = 3 // 无效,obg.a依然为1
// 此时obj的属性无法访问,自然也无法通过Object.defineProperty来实现响应式
Object.isFrozen(obj) // 返回true,检查是否冻结
冻结的对象不会被响应化,节约了observe该对象每个属性的资源(添加getter和setter)
举一个例子,可以做一个demo实际体验一下效率的区别,做个页面放两个按钮,分别绑定loadFrozenDatas和loadNormalDatas方法,就能明显感受到生成同样的数据,冻结的对象生成速度显著快于不冻结的对象生成。
data() {
return{
normalDatas: [],
freezeDatas: [],
};
},
methods: {
loadNormalDatas(){
this.normalDatas = this.getDatas(); console.log("normalDatas", this.normalDatas);
},
loadFrozenDatas() {
this.freezeDatas = Object.freeze(this.getDatas()); console.log("freezeDatas", this.freezeDatas);
},
getDatas(){
const result = [];
for (var i = 0; i < 1000000; i++) {
result.push({
id: i,
name:`name${i}`,
address:{
city:`city${i}`,
province:`province${i}`,
},
});
}
return result;
},
}
observe中会调用Object.defineProperty(),通过属性描述符为对象的每个属性实现响应式
属性描述符详情请看:属性描述符初探——Vue实现数据劫持的基础
三、使用函数式组件
在Vue.js中,函数式组件是一种没有状态和实例的概念的组件。函数式组件主要用于声明性地描述UI,它们接受 props 作为输入,并返回一个Vue元素树作为输出。
函数式组件不会通过new VueComponent生成新的vue实例,不会加入到vue的组件树中,只做页面渲染,节省性能。
// Vue 2中的函数式组件:
Vue.component('my-functional-component', {
functional: true,
render: function (createElement, context) {
// 使用createElement创建元素
return createElement('div', context.props.text);
}
});
// Vue 3中的函数式组件:
import { h, FunctionalComponent } from 'vue';
const MyFunctionalComponent: FunctionalComponent = (props, { slots }) => {
return h('div', props.text, slots().default);
};
export default MyFunctionalComponent;
四、使用计算属性
计算属性可以缓存(只有所依赖的数据变化了才会重新计算),如果模版中数据会使用多次,就可以使用计算属性。
五、使用非实时绑定的表单项
双向绑定会导致任意一端修改数据均会导致重渲染rerender,在不需要双向绑定的位置(比如只开放只读数据)或者不需要保持实时数据双向绑定的情况下(比如输入框内容和页面某元素绑定,输入过程中每按一次键盘都会导致一次重新渲染)不使用v-model。
也可以通过v-model.lazy来允许某一时间内数据与表单内容不一致,也就是从监听@input变成了监听@change。
六、保持对象引用稳定
6.1、保持对象引用稳定定义
大多数情况下,vue触发重渲染的时机是依赖数据发生变化的时机,若数据没有变化,哪怕重新给数据赋值,vue也不会做出反应。
因此,哪怕读取属性所属的对象值没变,但是引用变了,也会导致页面重新渲染。
6.2、保持对象引用稳定与不稳定的例子
现在页面上渲染了一个表格,由一系列对象数据生成。如果要在数据库增加一行。那么读取数据库增加的数据,并将其添加到现有的表格数据中,效率会比直接从数据库读取全部数据,然后赋值给表格绑定的数据上要高。
因为读取增加的数据,然后修改表格绑定的数据,只有变化的数据会重新渲染,原先有的表格行不会重渲染,可如果直接把增加后的全部数据(一个引用不同的新对象)赋值给表格绑定的数据,就会导致所有行全部重新渲染,哪怕大多数行数据并没有变化。
6.3、vue2判断数据是否变化是通过hasChanged函数实现的
function hasChanged(x : unknown, y : unknown) :boolean{
if (x === y){
return x === 0 && 1 / x !== 1 / (y as number)
} else {
return x === x || y === y
}
}
这里x与y分别是新值和旧值。
正常情况下,如果x===y,代表没有改变,返回false,反之则返回true。
①为什么在x === y成立的时候,还要做一个return x === 0 && 1 / x !== 1 / (y as number)的判断?
+0 === -0的判断恒为true,但二者实际不相等。所以先判断x是否为0+或0-,如果不是,则直接触发短路返回false,如果是,就通过求倒数,比较倒数是否相等,如果均为0+或均为0-,则依然返回false,若倒数为Infinity和-Infinity,这样就会返回true,从而排除从0+变为0-,但是却新旧值却相等的情况。
②为什么在 x === y 不成立的时候,还要做一个x === x || y === y的判断?
因为如果NaN === NaN的判断恒为false,所以哪怕不相等,还要做一下自判断,若新旧值都是NaN,则返回false。排除x与y都为NaN,但是新旧值却不相等的情况。
七、使用v-show替代v-if
对于频繁切换显示状态的元素,使用v-show可以保证虚拟dom树的稳定,尤其是对于那些内部包含大量dom元素的节点,这一点极其重要。
DOM树只与布局有关,与显示与否无关。使用v-show渲染的元素,不管返回值是什么,都会添加到dom树中,但是使用v-if渲染的元素,只有为true的时候才会添加到DOM树中。
八、使用延迟装载(defer)
HTML中的<script>标签有一个可选的defer属性。当脚本设置了defer属性后,它会被告知浏览器在文档解析完成后再执行这个脚本,而不是立即执行。
对于图片和其他资源,可以使用 v-lazy-image 或 v-lazy-component 这样的 Vue 指令来实现懒加载。
<!-- 在模板中使用 -->
<img v-lazy="imageSrc" alt="Lazy Image">
使用像 vue-lazyload 这样的库来实现图片和组件的懒加载。
// 安装 vue-lazyload
npm install vue-lazyload --save
// 在 main.js 中使用
import Vue from 'vue';
import VueLazyload from 'vue-lazyload';
Vue.use(VueLazyload, {
preLoad: 1.64,
error: 'error.png',
loading: 'loading.png',
attempt: 1
});
九、使用keep-alive
keep-alive 是 Vue 的一个内置组件,用于缓存不活动的组件实例,避免重复创建和销毁组件,从而提高性能。这在单页面应用(SPA)中特别有用,可以保持用户状态和避免不必要的重新渲染。
<!-- 在路由出口使用 -->
<router-view v-if="$route.meta.keepAlive"></router-view>
<keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</keep-alive>
这里会根据路由的 meta 属性决定是否使用 keep-alive。
十、长列表优化
海量数据渲染容易导致卡顿,除了可以运用第六点保持引用对象稳定外,Vue还提供了几种方法来优化长列表的渲染:
- 虚拟滚动(Virtual Scrolling):只渲染可视区域内的元素,而不是渲染整个列表。可以使用 vue-virtual-scroll-list 或 vue-virtual-scroller 等库来实现。
- 分页或无限滚动:将数据分批次加载,而不是一次性加载所有数据。
- 节流(Throttle)和防抖(Debounce):在处理滚动事件等频繁触发的操作时,使用节流或防抖技术减少事件处理的频率。
此外,element plus也提供了虚拟化组件,用来处理海量数据的渲染问题
十一、打包体积优化
减少最终打包文件的大小可以提高应用的加载速度和性能。常见方法包括:
- 代码分割(Code Splitting):使用 Webpack 的动态 import() 语法来实现代码分割,只加载用户实际需要的代码。
- Tree Shaking:移除未使用的代码,Webpack 等现代打包工具可以自动进行 Tree Shaking
- 使用 Vue CLI 的优化选项:Vue CLI 提供了多种优化配置,如 vue-cli-service build --report 可以生成报告,帮助分析打包体积。
- 移除 console.log:在生产环境中,确保移除所有的 console.log 语句,因为它们会增加打包体积。
- 使用 Terser 或 UglifyJS 压缩 JavaScript:这些工具可以压缩 JavaScript 代码,减少文件大小。
十二、总结与相关资源
Vue在处理少量数据和有限dom的情况下技术已经非常成熟了,但现在随着AI时代的到来,海量数据场景会越来越多,Vue优化技巧也是必备技能。
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
更多优质内容,请关注:
JS底层逻辑:
最细最有条理解析:事件循环(消息循环)是什么?进程与线程的定义、关系与差异
JS语法篇:
你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解
通过array.every()实现数据验证、权限检查和一致性检查,array.some与array.every的区别
通过array.some()实现权限检查、表单验证、库存管理、内容审查和数据处理
通过array.map()实现数据转换、创建派生数组、异步数据流处理、搜索和过滤等需求
通过array.reduce()实现数据汇总、条件筛选和映射、对象属性的扁平化、转换数据格式等
通过array.filter()实现数组的数据筛选、数据清洗和链式调用
巧妙算法与窍门:
多维数组操作,不要再用遍历循环foreach了,来试试数组展平的小妙招!
shpfile转GeoJSON且控制转化精度;如何获取GeoJSON?GeoJson结构详解
Mapbox添加行政区矢量图层、分级设色图层、自定义鼠标悬浮框、添加天地图底图等
Element plus拓展:
通过el-tree自定义渲染网页版工作目录,实现鼠标悬浮显示完整名称等