前言:
最近接触到有性能问题的vue项目,经过排查发现有一些编码上的一些优化的点,同时在网上也查阅了一些资料,总结了一些vue项目的注意事项:
实践:
-
v-if 和 v-show 区分使用场景
v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。 -
computed 和 watch 区分使用场景
computed:是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
-
watch:更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景: -
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
1)v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff
(2)v-for 遍历避免同时使用 v-if
v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。
推荐:1 2 3 4 5 6 7 8 9 10 11 12 13 14
<ul> <li v-for="user in activeUsers" :key="user.id"> {{ user.name }} </li> </ul> computed: { activeUsers: function () { return this.users.filter(function (user) { return user.isActive }) } }
不推荐:
1 2 3 4 5 6 7 8
<ul> <li v-for="user in users" v-if="user.isActive" :key="user.id"> {{ user.name }} </li> </ul>
-
keep-alive
keep-alive是Vue提供的一个比较抽象的组件,用来对组件进行缓存,从而节省性能。
-
渲染数据与使用数据分离
问题:
vue框架支持双向绑定,即我们定义在data属性中的数据,vue都会做getter和setter,但是通常在实际的业务中,并不是所有的data中定义的数据都会在页面渲染中用到,有一部分数据是我们在处理业务逻辑中为了方便定义在data中的,其实这部分数据是不需要进行监听的,因为定义在了data中,我们在修改这些数据的时,就会触发observer监听,从而有一些不必要的性能消耗
解决方法:
渲染数据与使用数据分离:在data中只定义页面需要渲染的数据
不需要渲染的数据放在created中进行初始化,在created中声明的数据不会进行getter和setter,从而避免了监听
不推荐:
1 2 3 4 5 6
data() { noUpdateData1: {...}, noUpdateData2: {...}, noUpdateData3: {...}, ... }
推荐:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
created() { initNoUpdateData(); }, methods: { initNoUpdateData() { this.noUpdateData1 = { ... }; this.noUpdateData2 = { ... }; this.noUpdateData3 = { ... }; } }
-
Object.freeze
Object.freeze()方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。
对于data()或vuex中冻结的对象,vue不会做getter和setter的转换。因此对于一个不变的、大数据量的数组或Object数据来说,使用Object.freeze()可以有效地提升性能。
1 2 3 4 5 6
data() { return { radioList: fdDataTable.radioList, selectList: fdDataTable.selectList } }
使用场景:页面中很多数据字典,下拉框数据、单选复选按钮数据等等,可以用该方法
1 2 3 4 5 6
data() { return { radioList: Object.freeze(fdDataTable.radioList), selectList: Object.freeze(fdDataTable.selectList) } }
-
避免对data中大数据量的属性,先赋值再改造。
栗子:
1 2 3 4 5
// 页面进来请求数据,请求完成:数据赋值 this.data = data; // 下面两个方法会对data数据进行判断,并对数据进行重组 this.fun1(); this.fun2();
导致的问题:
页面渲染是依赖data数据的,如果先进行赋值,则vue双向绑定会对页面进行重排或者重绘,下面又对该数据进行了修改,则会多次触发data的数据监听,并且进行多次的dom的重排或重绘,从而引发性能问题
解决方案:
上面将data当做下面两个方法的参数,在方法中进行修改,最后再进行this.data的赋值,这样会减少很多没必要的性能消耗
1 2 3 4 5
// 下面两个方法会对data数据进行判断,并对数据进行重组 this.fun1(data); this.fun2(data); // 页面进来请求数据,请求完成:数据赋值 this.data = data;
-
路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
路由懒加载:
1 2 3 4 5 6
const Foo = () => import('./Foo.vue') const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] })
-
第三方插件的按需引入
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助
babel-plugin-component
,然后可以只引入需要的组件,以达到减小项目体积的目的。以下为项目中引入 element-ui 组件库为例:(1)首先,安装
babel-plugin-component
:1
npm install babel-plugin-component -D
(2)然后,将 .babelrc 修改为:
1 2 3 4 5 6 7 8 9 10 11 12
{ "presets": [["es2015", { "modules": false }]], "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }
(3)在 main.js 中引入部分组件:
1 2 3 4 5
import Vue from 'vue'; import { Button, Select } from 'element-ui'; Vue.use(Button) Vue.use(Select)