自建博客文章链接:http://www.heblogs.cn/articleDetails/60fa1725b69f2b0a1af646d4
在日常Vue开发之中性能优化是重中之重的,那么本文作为我使用Vue框架的几个优化性能经验小结
一、代码优化
1、scope中 元素选择器尽量少用
在 scoped 样式中,类选择器比元素选择器更好,因为大量使用元素选择器是很慢的。为了给样式设置作用域,Vue 会为元素添加一个独一无二的特性,例如 data-v-f3f3eg9。然后修改选择器,使得在匹配选择器的元素中,只有带这个特性才会真正生效,(比如 button[data-v-f3f3eg9])。问题在于大量的元素和特性组合的选择器 (比如 button[data-v-f3f3eg9]) 会比类和特性组合的选择器
慢,所以应该尽可能选用类选择器。
参照:https://cn.vuejs.org/v2/style-guide/#scoped-中的元素选择器-谨慎使用
2、 单个vue文件过大,做组件提取、页面拆分和公共方法提取,减少冗余代码。
vue文件过大导致页面加载过慢,需要做组件的抽离,拆分成多个页面组件,比如tab页、封装公共API,暴露接口给各组件调用。
按模块划分路由。按照业务模块划分路由及子路由,保证模块间的隔离;
模块内按布局或功能划分组件。单个模块功能较多或逻辑较复杂时,按照布局划分为左右或上下及其组合模式划分组件,以子组件的方式放在模块中,方便打包优化、复用、功能拓展及维护;
能复用的组件尽量复用。布局、功能模块等均可复用,如:tab中的筛选条件、详情弹窗、表单等。
3、v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
-
v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。 -
v-for 遍历避免同时使用 v-if
v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。
<template>
<div class="home">
<ul>
<!--//推荐:正常使用唯一key值 -->
<li v-for="user in activeUsers" :key="user.id"> {{ user.name }</li>
<!--//不推荐:不使用key值 、 v-for和v-if搭配使用 -->
<li v-for="user in users" v-if="user.isActive" >{{ user.name }}</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return {
users:[
{id:1, name:'zhangsan', isActive:true},
{id:2, name:'lisi', isActive:true},
{id:3, name:'wangwu', isActive:false},
{id:4, name:'maliu', isActive:true},
]
}
},
computed: {
activeUsers(){ return this.users.filter(v=> v.isActive) }
}
}
</script>
4、v-if 和 v-show 区分使用场景
- v-if 是 真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
- v-show就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show则适用于需要非常频繁切换条件的场景。
5、computed 和 watch 区分使用场景
-
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取computed 的值时才会重新计算 computed 的值;
-
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态,这些都是计算属性无法做到的。但应该尽量减少watch的使用。
6、第三方插件的按需引入
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component ,然后可以只引入需要的组件,以达到减小项目体积的目的。
7、图片资源懒加载
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件:
1.安装插件
npm install vue-lazyload --save-dev
2.在入口文件 man.js 中引入并使用
import VueLazyload from 'vue-lazyload'
然后再 vue 中直接使用
Vue.use(VueLazyload)
或者添加自定义选项
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',
loading: 'dist/loading.gif',
attempt: 1
})
3.在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载显示
8、路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。这时候需要用到路由懒加载:
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [{ path: '/foo', component: Foo }]
})
9、 组件缓存keep-alive
vue提供了keep-alive标签来存储缓存组件,对于一些视频控件object或图表类的使用,我们经常会使用v-if指令,而v-if是会创建和销毁的,如果频繁操作在ie下的内存会持续上升,而keep-alive可以有效的缓存,抑制内存的持续上升vue-keep-alive。
10、长列表性能优化
Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。
export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
};
11、事件的销毁
Vue 组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。 如果在 js 内使用 addEventListene 等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露,如:
created() {
addEventListener('click', this.click, false)
},
beforeDestroy() {
removeEventListener('click', this.click, false)
}
12、 打包优化
若打包出的vender.js和app.js较大,App.js可通过组件懒加载解决,vender包该如何解决?
打包 vender 时不打包 vue、vuex、vue-router、axios 等,换用国内的 bootcdn 直接引入到根目录的 index.html 中。
<script src="//cdn.bootcss.com/vue/2.2.5/vue.min.js"></script>
<script src="//cdn.bootcss.com/vue-router/2.3.0/vue-router.min.js"></script>
<script src="//cdn.bootcss.com/vuex/2.2.1/vuex.min.js"></script>
<script src="//cdn.bootcss.com/axios/0.15.3/axios.min.js"></script>
在 webpack 里有个 externals,可以忽略不需要打包的库
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios'
}
二、用户体验优化
1、better-click防止iphone点击延迟
在开发移动端vuejs项目时,手指触摸时会出现300ms的延迟效果,可以采用better-click对ipone系列的兼容体验优化。
2、菊花loading
菊花loading,在加载资源过程之中,可以提供loading。此菊花loading不是那菊花。所以可以自由选择自己喜欢的菊花。参考:https://www.jb51.net/article/141079.htm
3、骨架屏加载
在首屏加载资源较多,可能会出现白屏和闪屏的情况。体验不好。盗图一波,小米商城使用骨架屏进行首屏在资源数据还没有加载完成时显示,给很好的体验效果。
参考:https://www.jianshu.com/p/cb5717c5948f
总结
项目构建前:
- 根据交互稿或视觉稿,合理划分pages,一般按照模块和子模块划分;
- 使用路由懒加载,实现模块化与按需加载的效果,优化首屏性能;
- 提取公共组件,尽量与业务隔离,如筛选条件等;
- 提取公共方法,如表单校验封装;
- 提取在项目中出现较多的功能模块,如弹窗详情等;
- 与后台开发人员定义接口协议,模块中能合并的尽量合并。
项目构建中和完成后:
- 看到错误和警告信息及时解决;
- 打开浏览器调试窗口,查看Network中从发起网页页面请求Request后分析HTTP请求后得到的各个请求资源信息(包括状态、资源类型、大小、所用时间等),可以根据这个进行网络性能优化。如查看哪些http请求返回资源非常大或耗用时间较长,可针对处理;
- 打开浏览器调试窗口,在Timeline中记录并分析在网站的生命周期内所发生的各类事件,以此可以提高网页的运行时间的性能;
- 代码编写过程中不断思考是否可优化模块或做共性组件提取;
- 及时代码审核,提高代码质量;