searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

使用Object.freeze优化VUE数据初始化

2023-05-23 06:20:51
29
0

在 Vue 组件初始化数据时,会递归遍历在 data 中定义的每一条数据,通过Object.defineProperty将数据改成响应式,这就意味着如果 data 中的数据量很大的话,在初始化时将会使用很长的时间去执行Object.defineProperty, 也就会带来性能问题,这个时候我们可以强制使数据变为非响应式,从而节省时间,看下这个例子:

<template>
<div>
<ul>
<li v-for="item in heavyData" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
// 一万条数据
const heavyData = Array(10000)
.fill(null)
.map((_, idx) => ({ name: 'test', message: 'test', id: idx }))
 
export default {
data() {
return {
heavyData: heavyData
}
},
beforeCreate() {
this.start = Date.now()
},
created() {
console.log(Date.now() - this.start)
}
}

heavyData中有一万条数据,这里统计了下从beforeCreatecreated经历的时间,对于这个例子而言,这个时间基本上就是初始化数据的时间。

这个时间一直在40-50ms,然后我们通过Object.freeze()方法,将heavyData变为非响应式的再试下:

data() {
return {
heavyData: Object.freeze(heavyData)
}
}

改完之后再试下,初始化数据的时间变成了0-1ms,快了有40ms,这40ms都是递归遍历heavyData执行Object.defineProperty的时间。

那么,为什么Object.freeze()会有这样的效果呢?对某一对象使用Object.freeze()后,将不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。

而 Vue 在将数据改造成响应式之前有个判断:

export function observe(value, asRootData) {
// ...省略其他逻辑
if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
// ...省略其他逻辑
}

这个判断条件中有一个Object.isExtensible(value),这个方法是判断一个对象是否是可扩展的,由于我们使用了Object.freeze(),这里肯定就返回了false,所以就跳过了下面的过程,自然就省了很多时间。

实际上,不止初始化数据时有影响,你可以用上面的例子统计下从created到mounted所用的时间,在我的电脑上不使用Object.freeze()时,这个时间是60-70ms,使用Object.freeze()后降到了40-50ms,这是因为在渲染函数中读取heavyData中的数据时,会执行到通过Object.defineProperty定义的getter方法,Vue 在这里做了一些收集依赖的处理,肯定就会占用一些时间,由于使用了Object.freeze()后的数据是非响应式的,没有了收集依赖的过程,自然也就节省了性能。

由于访问响应式数据会走到自定义 getter 中并收集依赖,所以平时使用时要避免频繁访问响应式数据,比如在遍历之前先将这个数据存在局部变量中,尤其是在计算属性、渲染函数中使用,关于这一点更具体的说明,你可以看黄奕老师的这篇文章:Local variables

但是这样做也不是没有任何问题的,这样会导致heavyData下的数据都不是响应式数据,你对这些数据使用computed、watch等都不会产生效果,不过通常来说这种大量的数据都是展示用的,如果你有特殊的需求,你可以只对这种数据的某一层使用Object.freeze(),同时配合使用上文中的延迟渲染、函数式组件等,可以极大提升性能。

0条评论
0 / 1000
Austin
3文章数
0粉丝数
Austin
3 文章 | 0 粉丝
Austin
3文章数
0粉丝数
Austin
3 文章 | 0 粉丝
原创

使用Object.freeze优化VUE数据初始化

2023-05-23 06:20:51
29
0

在 Vue 组件初始化数据时,会递归遍历在 data 中定义的每一条数据,通过Object.defineProperty将数据改成响应式,这就意味着如果 data 中的数据量很大的话,在初始化时将会使用很长的时间去执行Object.defineProperty, 也就会带来性能问题,这个时候我们可以强制使数据变为非响应式,从而节省时间,看下这个例子:

<template>
<div>
<ul>
<li v-for="item in heavyData" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
// 一万条数据
const heavyData = Array(10000)
.fill(null)
.map((_, idx) => ({ name: 'test', message: 'test', id: idx }))
 
export default {
data() {
return {
heavyData: heavyData
}
},
beforeCreate() {
this.start = Date.now()
},
created() {
console.log(Date.now() - this.start)
}
}

heavyData中有一万条数据,这里统计了下从beforeCreatecreated经历的时间,对于这个例子而言,这个时间基本上就是初始化数据的时间。

这个时间一直在40-50ms,然后我们通过Object.freeze()方法,将heavyData变为非响应式的再试下:

data() {
return {
heavyData: Object.freeze(heavyData)
}
}

改完之后再试下,初始化数据的时间变成了0-1ms,快了有40ms,这40ms都是递归遍历heavyData执行Object.defineProperty的时间。

那么,为什么Object.freeze()会有这样的效果呢?对某一对象使用Object.freeze()后,将不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。

而 Vue 在将数据改造成响应式之前有个判断:

export function observe(value, asRootData) {
// ...省略其他逻辑
if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
// ...省略其他逻辑
}

这个判断条件中有一个Object.isExtensible(value),这个方法是判断一个对象是否是可扩展的,由于我们使用了Object.freeze(),这里肯定就返回了false,所以就跳过了下面的过程,自然就省了很多时间。

实际上,不止初始化数据时有影响,你可以用上面的例子统计下从created到mounted所用的时间,在我的电脑上不使用Object.freeze()时,这个时间是60-70ms,使用Object.freeze()后降到了40-50ms,这是因为在渲染函数中读取heavyData中的数据时,会执行到通过Object.defineProperty定义的getter方法,Vue 在这里做了一些收集依赖的处理,肯定就会占用一些时间,由于使用了Object.freeze()后的数据是非响应式的,没有了收集依赖的过程,自然也就节省了性能。

由于访问响应式数据会走到自定义 getter 中并收集依赖,所以平时使用时要避免频繁访问响应式数据,比如在遍历之前先将这个数据存在局部变量中,尤其是在计算属性、渲染函数中使用,关于这一点更具体的说明,你可以看黄奕老师的这篇文章:Local variables

但是这样做也不是没有任何问题的,这样会导致heavyData下的数据都不是响应式数据,你对这些数据使用computed、watch等都不会产生效果,不过通常来说这种大量的数据都是展示用的,如果你有特殊的需求,你可以只对这种数据的某一层使用Object.freeze(),同时配合使用上文中的延迟渲染、函数式组件等,可以极大提升性能。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0