vue性能优化
1.运行时优化
1.1引入生产环境vue文件
开发环境下,Vue 会提供很多警告来帮你对付常见的错误与陷阱。而在生产环境下,这些警告语句没有用,反而会增加应用的体积。有些警告检查还有一些小的运行时开销。
当使用 webpack 或 Browserify 类似的构建工具时,Vue 源码会根据 process.env.NODE_ENV 决定是否启用生产环境模式,默认情况为开发环境模式。在 webpack 与 Browserify 中都有方法来覆盖此变量,以启用 Vue 的生产环境模式,同时在构建过程中警告语句也会被压缩工具去除。
详细的做法请参阅 [生产环境部署(https://cn.vuejs.org/v2/guide/deployment.html)
1.2 使用单文件组件预编译模板(模块化开发)
当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。通常情况下这个过程已经足够快了,但对性能敏感的应用还是最好避免这种用法。用.vue的单文件拆分模块。
预编译模板最简单的方式就是使用单文件组件——相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串。
官方例子:todo应用
详细的做法请参阅 预编译模板
1.3 提取组件的 CSS 到单独到文件
当使用单文件组件时,组件内的 CSS 会以 style标签的方式通过 JavaScript 动态注入。这有一些小小的运行时开销,将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存。
1.4 利用Object.freeze()提升性能
Object.freeze() 可以冻结一个对象,冻结之后不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。该方法返回被冻结的对象。
当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter,这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
但 Vue 在遇到像 Object.freeze() 这样被设置为不可配置之后的对象属性时,不会为对象加上 setter getter 等数据劫持的方法。`
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
if (!getter && arguments.length === 2) {
val = obj[key]
}
const setter = property && property.set
`
例子:big table benchmark,用了freeze快了4倍
1.5 扁平化 Store 数据结构
很多时候,我们会发现接口返回的信息是如下的深层嵌套的树形结构:
{
“id”: “123”,
“author”: {
“id”: “1”,
“name”: “Paul”
},
“title”: “My awesome blog post”,
“comments”: [
{
“id”: “324”,
“commenter”: {
“id”: “2”,
“name”: “Nicole”
}
}
]
}
复制代码假如直接把这样的结构存储在 store 中,如果想修改某个 commenter 的信息,我们需要一层层去遍历找到这个用户的信息,同时有可能这个用户的信息出现了多次,还需要把其他地方的用户信息也进行修改,每次遍历的过程会带来额外的性能开销。
假设我们把用户信息在 store 内统一存放成 users[id]这样的结构,修改和读取用户信息的成本就变得非常低。
你可以手动去把接口里的信息通过类似数据的表一样像这样存起来,也可以借助一些工具,这里就需要提到一个概念叫做 JSON数据规范化(normalize), Normalizr 是一个开源的工具,可以将上面的深层嵌套的 JSON 对象通过定义好的 schema 转变成使用 id 作为字典的实体表示的对象。
举个例子,针对上面的 JSON 数据,我们定义 users comments articles 三种 schema:
import {normalize, schema} from ‘normalizr’;
// 定义 users schema
const user = new schema.Entity(‘users’);
// 定义 comments schema
const comment = new schema.Entity(‘comments’, {
commenter: user,
});
// 定义 articles schema
const article = new schema.Entity(‘articles’, {
author: user,
comments: [comment],
});
const normalizedData = normalize(originalData, article);
复制代码normalize 之后就可以得到下面的数据,我们可以按照这种形式存放在 store 中,之后想修改和读取某个 id 的用户信息就变得非常高效了,时间复杂度降低到了 O(1)。
{
result: “123”,
entities: {
“articles”: {
“123”: {
id: “123”,
author: “1”,
title: “My awesome blog post”,
comments: [ “324” ]
}
},
“users”: {
“1”: { “id”: “1”, “name”: “Paul” },
“2”: { “id”: “2”, “name”: “Nicole” }
},
“comments”: {
“324”: { id: “324”, “commenter”: “2” }
}
}
}
复制代码需要了解更多请参考 normalizr 的 文档
1.6 避免持久化 Store 数据带来的性能问题
当你有让 Vue App 离线可用,或者有接口出错时候进行灾备的需求的时候,你可能会选择把 Store 数据进行持久化,这个时候需要注意以下几个方面:
1.6.1 持久化时写入数据的性能问题
Vue 社区中比较流行的 vuex-persistedstate,利用了 store 的 subscribe 机制,来订阅 Store 数据的 mutation,如果发生了变化,就会写入 storage 中,默认用的是 localstorage 作为持久化存储。
也就是说默认情况下每次 commit 都会向 localstorage 写入数据,localstorage 写入是同步的,而且存在不小的性能开销,如果你想打造 60fps 的应用,就必须避免频繁写入持久化数据。
下面是开发环境下通过 Performance 工具抓取的一个截图,可以看到出现了一次长达 6s 的卡顿:
6秒钟的卡顿
通过 Bottom-Up 可以看到 setState 占用了 3241.4ms 的 CPU 执行时间,而 setState 正是在向 Storage 写入数据。
vuex-persistedstate setState 源码
我们应该尽量减少直接写入 Storage 的频率:
多次写入操作合并为一次,比如采用函数节流或者将数据先缓存在内存中,最后在一并写入
只有在必要的时候才写入,比如只有关心的模块的数据发生变化的时候才写入
1.6.2 避免持久化存储的容量持续增长
由于持久化缓存的容量有限,比如 localstorage 的缓存在某些浏览器只有 5M,我们不能无限制的将所有数据都存起来,这样很容易达到容量限制,同时数据过大时,读取和写入操作会增加一些性能开销,同时内存也会上涨。
尤其是将 API 数据进行 normalize 数据扁平化后之后,会将一份数据散落在不同的实体上,下次请求到新的数据也会散落在其他不同的实体上,这样会带来持续的存储增长。
因此,当设计了一套持久化的数据缓存策略的时候,同时应该设计旧数据的缓存清除策略,例如请求到新数据的时候将旧的实体逐个进行清除。
以上是 vue性能优化 的全部内容, 来源链接: utcz.com/z/377252.html