Vue常用性能优化
vue 常用的一下优化方式,主要是在构建项目过程中需要注意的方面
1、编码优化
1.1、避免相应所有数据
不要将所有的数据都放到 data 中, data 中的数据会增加 getter 和 setter ,并且会收集 watcher ,这样还占内存,不需要响应式的数据我们可以直接定义在实例上。
1.2、函数式组件
函数组是一个不包含状态和实例的组件,简单来说,就是组件不支持响应式,并且不能通过 this 关键字引用自己。因为函数式组件没有状态,所以它们不需要像 Vue 的响应式系统一样需要经历额外的初始化,这样就可以避免相关操作带来的性能消耗。当然函数式组件仍会对相应的变化做出响应式改变,比如新传入新的 props ,但是在组件本身中,它无法知道数据何时发生了更改,因为它不维护自己的状态。很多场景非常适合使用函数式组件:
- 一个简单的展示组件,也就是所谓的 dumb 组件。例如 buttons 、 pills 、 tags 、 cards 等,甚至整个页面都是静态文本,比如 About 页面。
- 高阶组件,即用于接收一个组件作为参数,返回一个被包装过的组件。
- v-for 循环中的每项通常都是很好的候选项。
1.3、区分computed和watch使用场景
computed 是计算属性,依赖其他属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
watch 更多的是观察的作用,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调后进行后续操作。
当我们需要进行数值计算,并依赖于其他数据时,应该使用 computed ,因为可以利用 computed 的缓存特性,避免每次获取值时,都需重新计算。当我们需要在数据变化时执行异步或开销较大的操作时,应使用 watch ,使用 watch 选项允许我们执行异步操作,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。
1.4、v-for添加key且避免同时使用v-if
- v-for 遍历必须为 item 添加 key ,且尽量不要使用 index 而要使用唯一 id 去标识 item ,在列表数据进行遍历渲染时,设置唯一 key 值方便 Vue.js 内部机制精准找到该条列表数据,当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。
- v-for 遍历避免同时使用 v-if , v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度。
1.5、区分v-if与v-show使用场景
- 实现方式: v-if 是动态的向 DOM 树内添加或删除 DOM 元素, v-show 是通过设置 DOM 元素的 display 样式属性控制显隐。
- 编译过程: v-if 切换有一个局部编译卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件, v-show 只是简单的基于 CSS 切换。
- 编译条件: v-if 是惰性的,如果初始条件为假,则什么也不做,只有在条件第一次变为真时才开始局部编译, v-show 是在任何条件下都被编译,然后被缓存,而且 DOM 元素保留。
- 性能消耗: v-if 有更高的切换消耗, v-show 有更高的初始渲染消耗。
- 使用场景: v-if 适合条件不太可能改变的情况, v-show 适合条件频繁切换的情况。
1.6、长列表性能化
Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。对于需要修改的长列表的优化,大列表两个核心,一个分段一个分区,具体执行分为:仅渲染视窗可见的数据、进行函数节流、减少驻留地 VNode 和 Vue 组件,不适用显示的子组件 slot 方式,改为手动创建虚拟 DOM 来切断对象引用。
export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
}
1.7、路由懒加载
Vue 是单页面应用,可能会有很多的路由引入,这样使用 webpack 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加有效。对于 Vue 路由懒加载的方式有 Vue 异步组件、动态 import 、 webpack 提供的 require.ensure ,最常用的就是动态 import 的方式。
{
path: '/home',
name: 'home',
// 打包后,每个组件单独生成一个chunk文件
component: () => import("@/view/home.vue")
}
1.8、使用keep-alive组件
当在组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题,使用 <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。重新创建动态组件的行为通常是非常有用的,但是在有些情况下我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来,此时使用 <keep-alive> 包裹组件即可缓存当前组件实例,将组件缓存到内存,用于保留组件状态或避免重新渲染,和 <transition> 相似的,其本身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
<keep-alive>
<component v-bind:is="currentComponent" class="tab"></component>
</keep-alive>
2、打包优化
2.1、模版预编译
当使用 DOM 内模版或 JavaScript 内的字符串模版时,模版会在运行时被编译为渲染函数,通常情况下这个过程已经足够快了,但对性能敏感的应用还是最好避免这种方法。预编译模版最简单的方式就是使用单文件组件——相关的构建设置会自动把编译处理好,所以侯建好的代码已经好汉了编译出来的渲染函数而不是原始的模版字符串。如果使用 webpack ,并且喜欢分离 JavaScript 和模版文件,可以使用 vue-template-loader ,其可以在构建过程中把模版文件转换成为 JavaScript 渲染函数。
2.2、SourceMap
在项目进行打包后,会将开发中的多个文件代码打包到一个文件夹中,并且经过压缩、去掉多余的空格、 babel 编译化后,最终将编译得到的代码会用在线上环境,那么这样处后的代码和源代码会有很大的差别,当有 bug 的时候,我们只能定位到压缩处理后的代码位置,无法定位到开发环境中的代码,对于开发来说不好调试定位问题,因此 sourceMap 出现了,它就是为了解决不好调试代码问题的,在线上环境则需要关闭 sourceMap 。
2.3、配置splitChunksPlugins
Webpack 内置了专门用于提取多个 Chunk 中的公共部分的插件 CommonsChunkPlugin ,用于提取公共代码的工具, CommonChunkPlugin 于 4.0 及以后被移除,使用 SplitChunksPlugin 代替。
2.4、使用treeShaking
tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码 dead-code ,其依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export ,这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup 。
2.5、第三方插件的按需引入
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component ,然后可以只引入需要的组件,以达到减少体积的目的,以项目中引入 element-ui 组件库为例。
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
import Vue from 'vue';
import { Button, Select } from 'element-ui'
Vue.use(Button)
Vue.use(Select)