Vue 项目性能优化
Vue项目性能优化
前言
Vue框架通过数据双向绑定和虚拟DOM技术,帮我们处理了前端开发中最脏最累的DOM操作部分,我们不再需要去考虑如何操作DOM以及如何最高效的操作DOM,但是vue项目中仍然存在项目首屏优化、webpack编译配置优化等问题,所以我们仍然需要去关注Vue项目性能方面的优化,使项目具有更高效的性能、更好的用户体验。
本文着重从代码层面讲述优化的内容。
一.代码层面的优化
- v-if 和v-show的区别
v-if是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当的被销毁和重建,也是惰性的。如果在初始渲染条件为假时,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
v-show就简单的多,不管初始条件是什么,元素总会被渲染,并且只是简单的基于css的display属性进行切换。
所以,v-if适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show则适用于需要非常频繁切换条件的场景。
2.Computed和watch的区别
computed:计算属性,依赖其他属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值才会重新计算computed的值。
watch: 更多的是观察的作用,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。
当需要进行数值计算,并且依赖于其它数据时,应该使用computed,因为可以利用computed的缓存特性,避免每次获取值时,都要重新计算。当需要在数据变化时执行异步或者开销较大的操作时,应该使用watch,使用watch选项允许我们执行异步操作(访问一个API),限制我们执行该操作的频率,并在得到最终结果前,设置中间状态。这都是计算属性无法做到的。
3. v-for遍历必须为item添加key,且避免同时使用v-if
v-for遍历必须为item添加key在列表数据进行遍历渲染时,需要为每一项item设置唯一key值,方便vue内部机制精准找到该条列表数据。当state更新时,新的状态值和旧的状态值对比,较快的定位到diff。建议使用item的id做为key值,尽量不要使用index,因为索引时按位置排序的,没办法唯一确定节点。
v-for遍历避免同时使用v-if。v-for比v-if优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当只需要渲染很小一部分的时侯,必要情况下应该替换成computed。
3.x版本中v-if总是优先于v-for,且key不再是必须的,因为vue会自动生成唯一的key。
1 <ul> 2 <li v-for="user in activeUsers" 3 :key="user.id"> 4 {{username}} 5 </li> 6 </ul>
1 computed:{ 2 activeUsers(){ 3 return this.users.filter(function(user){ 4 return user.isActive 5 }) 6 } 7 }
4. 长列表性能优化
Vue会通过Object.defineProperty对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,就不需要vue来劫持数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,可以通过object.freeze方法来冻结一个对象,一旦冻结就再也不能修改。
5. 事件的销毁
Vue组件销毁时,会自动清理它与其他实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。如果在js内使用addEventListener等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露。
6. 路由懒加载
Vue是单页面应用,可能会有很多的路由引入,这样使用webpack打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才能加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面速度会降下来。
1 const Loading = ()=>import('@/view/loading/Index') 2 const router = new Router({ 3 routes:[ 4 { path: 'loading',component: Loading} 5 ] 6 })
1 { 2 path: '/apps-manage', 3 name: 'appsManage', 4 component: () => import('@/views/appsManage/index'), 5 meta: { title: '应用包管理', parent: '/apps-manage', crumb: '应用包管理' }, 6 }
1 { 2 path: 'loading', 3 name: 'Loading', 4 component: resolve => require(['@/view/loading/Index'],resolve) 5 }
7. 第三方插件按需引入