这个命题也是非常广,下面就例举一些在日常工作中常用的一些 Vue 代码层面的优化手段。
1 - 路由懒加载
const router = new VueRouter({
routes: [
{
path: "/",
component: () => import('@/views/Home.vue')
}
]
})
相信这个操作大家在日常工作中也是经常用到的。
2 - keep-alive 缓存页面
相信大家也会经常听到 keep-alive 这个名词,但它到底是什么呢?
事实上,在日常的开发中,很多组件并不需要每次都初始化,所以,为了优化性能,我们希望能将组件进行持久化,维持它的状态,防止每次都重新初始化。
而 keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染 。
这也就是常说的组件缓存了。
keep-alive 使用起来也是非常方便的:
<template>
<div id="root">
<keep-alive>
<router-view />
</keep-alive>
</div>
<template/>
keep-alive 可以接受两个参数,方便更加细节的操作:
- include: 只有匹配的组件会被缓存
- exclude: 任何匹配的组件都不会被缓存
3 - 使用 v-show 复用 DOM
大家都知道,在日常开发中,v-show 使用的情况是非常少的。因为与 v-if 相比,它仅仅是用 display: none 将节点隐藏了起来,实际上节点依然是存在的,一来这样实际上会增加内存成本,二来在权限管理的情况下,用户只要调出调试就可以绕过,非常不安全。
但这并意味着 v-show 就真的一无是处,相反在某些情况下,使用 v-show 的性能是优于 v-if 的,我们来看个例子:
<template>
<div class="cell">
<div v-show="isOn" class="on">
<Heavy :n="999999">
</div>
</div>
</template>
假如 Heavy 是一个数据量庞大,并且逻辑复杂的组件,如果使用 v-if,则每次满足条件的时候,都需要将 Heavy 渲染一遍,而 v-show 则会将节点保存下来,当满足条件的时候直接改变其 display 令其渲染即可:这是典型的空间换时间的操作。
4 - 避免同时使用 v-if 和 v-for
这个问题在前面的 [v-for 和 v-if 谁的优先级更高](www.notion.so/005-v-for-v…) 有提到过,具体方法则是利用计算属性对目标数组进行过滤即可:
<template>
<ul>
<li v-for="user in activeUsers" :key="user.id">{{ user.name }}</li>
</ul>
</template>
<script>
export default {
data: () => {
return {
users: [
{ name: "alice", isActive: false },
{ name: "bob", isActive: true },
{ name: "cindy", isActive: true }
]
};
},
computed: {
activeUsers: () => {
return this.users.filter(user => user.isActive);
}
}
};
</script>
5 - 长列表性能优化
对于长列表,通常分两种情况来优化。
- 一是静态列表:如果这个列表仅仅用于数据的展示,不会有任何数据变化,那么就不需要作响应式处理。但由于 vue data 天生就是响应式的,所以我们可以利用
Object.freeze将其冻结起来。(事实上也可以直接用Object.defineProperty直接去掉响应式)
export default {
data: () => {
return {
users: [
/* a long static list */
]
};
},
async create() {
const users = await axios.get("/users");
this.users = Object.freeze(users);
}
};
- 二是虚拟滚动:对于大数据的长列表,如果一次性全部渲染,显然是非常消耗性能的,所以我们可以采用虚拟滚动技术,只渲染被展示出来的部分(这方面的库有:
vue-virtual-scroller, vue-virtual-scroll-list,具体使用参考文档即可)
<recycle-scroller class="items" :items="items" :item-size="12">
<template>
<FetchItemView :item="item" @vote="voteItem(item)"/>
</template>
</recycle-scroller>
6 - 销毁事件避免内存泄漏
我们知道,在 vue 组件销毁的时候,会自动解绑它的全部指令以及事件监听器,但这仅限于组件本身的事件,不包括定时器。所以如果一个组件有使用到定时器,最好在组件销毁的时候同时销毁定时器,防止内存泄漏。
export default {
created() {
this.timer = setInterval(this.refresh, 1000);
},
beforeDestroy() {
clearInterval(this.timer);
}
};
7 - 图片懒加载
图片加载是个老生常谈的问题了,在 vue 中也有非常好用的库(如 vue-lazyload)可以方便的实现这个功能。(事实上 chrome 也支持使用 lazyload 方便的设置图片懒加载)
<img v-lazy="/static/img/01.png"/>
8 - 按需引入第三方插件
相信大家平时在使用第三方组件库的时候都是直接整体引入的,比如下面例子中的 element ui:
import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
事实上很多项目并不会用到组件库中的所有组件,这样的引入显然是有优化空间的,比如向下面这样按需引入(这点 antd 是真的很好):
import Vue from 'vue'
import { Button, Input } from 'element-ui';
Vue.use(Button);
Vue.use(Input);
9 - 无状态组件
这个可能平时用到的并不是很多,对于没有状态,完全用于展示的组件来说,我们可以利用 functional 将其设置为无状态组件,也就类似于 react 中的函数组件(没有 hooks 之前)
<template functional>
<div class="cell">{{ value }}</div>
</template>
<script>
export default {
props: ["value"]
};
</script>
10 - 变量本地化
这个技巧不仅仅在 vue 中,在其他地方也都非常常用。
export default {
props: ["start"],
computed: {
base: () => 42,
result: () => {
const base = this.base;
let result = this.start;
for (let i = 0; i < 99999; i++) {
result += heavy(base); // 这里是复杂的运算
}
return result;
}
}
};
可以预见,如果这里没有将 base 本地化保存,每次取用 base 都会经过一系列复杂的 vue 内部流程,性能消耗和取一个本地静态变量是不可同日而语的。
事实上,在函数的开始将需要用到的变量本地化保存是一个非常不错的习惯。
11 - SSR
这也是一个非常热门的话题,vue 的话结合 nuxt 可以很好的做到同构开发。
而 SSR 显著的优势主要有两点:
- 优化 SEO
- 优化首屏渲染速度