Vue3 和 Vue2 的响应式哪个性能更好?
面试题处处有坑,让人防不胜防,不提前刷下面试题大概率会被面试官问的一脸闷逼。
面试官问:Vue3 和 Vue2 的响应式性能上哪个更好?
我心想:这面试官不按套路出牌啊,不是应该问“Vue3 响应式的区别”、“解决了 Vue2 响应式的什么问题”之类的么? 突然问起了性能,这还用问么?当然是 Vue3 响应式的性能更好啊。难道 Vue 升级还往性能差的方向升级?
我:Vue3 响应式性能更好
面试官微微一笑(掉坑里了)问道:为什么?
我:Vue3 响应性采用的是 ES6 Proxy,现在主流浏览器都支持,浏览器支持的要比用 JavaScript 代码实现的性能好点吧!而且 Proxy 不用遍历对象的属性来添加响应。
面试官:Proxy 是在这方面有优势,但是事物都是有两面性的,有优点同样也会有缺点,Vue2 打磨优化了这么多年,早已有一套性能最优的方案来处理和弱化 Object.defineProperty()缺点带来的影响。而 Vue3 的 Proxy 出来不久,在 Proxy 的缺点上,Vue3 还需要长期的打磨和调优,Vue3 响应式的性能未必就比 Vue2 的好。
我:...啊!还有这种角度?
于是这次面试挂了。。。。。
响应式性能分为两部分:响应式初始化性能、响应式视图更新性能。
响应式初始化
Vue2 响应式初始化
<div id="app"></div>
<script>
const len = 1;
const arr = Array(len);
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: "test" };
}
console.time("Vue2 init");
new Vue({
el: "#app",
data() {
return {
arr,
};
},
created() {
console.timeEnd("Vue2 init");
},
});
</script>
数组长度为 1 时初始化时间为:1ms,内存的是使用内存的使用情况得:1.5MB
数组长度 | 初始化时间 | 内存 |
---|---|---|
1 | 1ms | 1.5MB |
1000 | 6ms | 2.4MB |
100000 | 162ms | 57MB |
1000000 | 1443ms | 569MB |
从表中可以看到随着数组的长度增加,初始化时间和内存使用量有明细的增加。
Vue3 响应式初始化
const { createApp } = Vue;
const len = 1;
const arr = new Array(len);
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: "test" };
}
console.time("Vue3 init");
createApp({
data() {
return {
arr,
};
},
created() {
console.timeEnd("Vue3 init");
},
}).mount("#app");
性能:
数组长度 | 初始化时间 | 内存 |
---|---|---|
1 | 4ms | 1.2MB |
1000 | 3ms | 2.5MB |
100000 | 4ms | 6.8MB |
1000000 | 4ms | 25MB |
数组长度增加初始化时间几乎没什么变化,内存使用相对 Vue2 也少很多。
小结
Vue2 使用的 Object.defineProperty()创建的响应式需要变量数组中的每一个项,并添加 get 和 set 方法在创建 Observer 和 dep 实例,因此内存和初始化时间随着数组长度增加而猛涨。
Vue3 使用 Proxy 实现响应式,不会遍历数组,读取对象属性时,才会创建 Dep 实例,属性值为对象时再将对象响应化,实现懒响应式,因此创建的实例也不多,因此初始化时间随着数组长度增加几乎不变,内存也增长不多。 大数据量响应式初始化上 Vue3 的性能要高于 Vue2。
响应式视图更新
Vue2 响应式视图更新
<div id="app">
<div>{{ arr.length }}</div>
<button @click="onClick">click</button>
</div>
<script>
const len = 1000000;
const arr = Array(len);
for (let i = 0; i < len; i++) {
arr[i] = { id: 0, name: "test" };
}
console.time("Vue2 init");
new Vue({
el: "#app",
data() {
return {
arr,
};
},
updated() {
console.timeEnd("Vue2 update");
},
methods: {
onClick() {
console.time("Vue2 update");
for (let i = 0; i < 50; i++) this.arr.unshift({ a: 1 });
},
},
});
</script>
性能:
数组长度 | 更新时间 |
---|---|
1 | 1ms |
1000 | 4ms |
100000 | 304ms |
1000000 | 3293ms |
Vue3 响应式视图更新
<div id="app">
<div>{{ arr.length }}</div>
<button @click="onClick">click</button>
</div>
<script>
const { createApp } = Vue;
const len = 1;
const arr = new Array(len);
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: "test" };
}
createApp({
data() {
return {
arr,
};
},
updated() {
console.timeEnd("Vue3 update");
},
methods: {
onClick() {
console.time("Vue3 update");
for (let i = 0; i < 50; i++) this.arr.unshift({ id: i, name: "test" });
},
},
}).mount("#app");
</script>
性能:
数组长度 | 更新时间 |
---|---|
1 | 5ms |
1000 | 75ms |
100000 | 6708ms |
1000000 | 70980ms |
小结
Vue3 在大数据量下 unshift 操作更新极慢,而造成这么慢的原因是 unshift 操作。我们在试试 push 操作性能如何:
<div id="app">
<div>{{ arr.length }}</div>
<button @click="onClick">click</button>
</div>
<script>
const { createApp } = Vue;
const len = 1;
const arr = new Array(len);
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: "test" };
}
createApp({
data() {
return {
arr,
};
},
updated() {
console.timeEnd("Vue3 update");
},
methods: {
onClick() {
console.time("Vue3 update");
for (let i = 0; i < 50; i++) this.arr.push({ id: i, name: "test" });
},
},
}).mount("#app");
</script>
性能:
数组长度 | 更新时间 |
---|---|
1 | 2ms |
1000 | 1ms |
100000 | 2ms |
1000000 | 4ms |
可以看到 Vue3 的 push 操作很快,而 unshift 操作反而非常慢,是因为 unshift 操作改变了数组中元素的索引,所有数组项目都必须重新分配给索引+1,也就是 50*数组长度的开销。 同理在删除数组前置项也有同样性能问题。
如何解决 unshift 操作的性能问题:
1、将多次 unshift 合并为一次 unshift
<div id="app">
<div>{{ arr.length }}</div>
<button @click="onClick">click</button>
</div>
<script>
const { createApp } = Vue;
const len = 1000000;
const arr = new Array(len);
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: "test" };
}
createApp({
data() {
return {
arr,
};
},
updated() {
console.timeEnd("Vue3 update");
},
methods: {
onClick() {
console.time("Vue3 update");
const list=[]
for (let i = 0; i < 50; i++) list.push({ a: 1 });
this.arr.unshift(...list)
},
},
}).mount("#app");
性能:
数组长度 | 更新时间 |
---|---|
1 | 1ms |
1000 | 5ms |
100000 | 164ms |
1000000 | 1828ms |
将 unshift 合并一次操作后更新时间快了很多,但实际项目中肯定会出现多次删除和 unshift 操作。
2、将数组不进行深度响应
<div id="app">
<div>{{ arr.length }}</div>
<button @click="onClick">click</button>
</div>
<script>
const { createApp, markRaw } = Vue;
const len = 1000000;
const arr = new Array(len);
for (let i = 0; i < len; i++) {
arr[i] = { id: i, name: "test" };
}
createApp({
data() {
return {
arr: markRaw(arr),
};
},
updated() {
console.timeEnd("Vue3 update");
},
methods: {
onClick() {
console.time("Vue3 update");
for (let i = 0; i < 50; i++) this.arr.unshift({ a: 1 });
this.arr = markRaw([...this.arr]);
},
},
}).mount("#app");
</script>
性能:
数组长度 | 更新时间 |
---|---|
1 | 1.2ms |
1000 | 2ms |
100000 | 7ms |
1000000 | 45ms |
这种方式更新时间更快,但是这种只适用于列表展示,修改都要重新赋值。
总结
Vue3 的响应式性能并不是完全比 Vue2 的好,在对数组响应式处理上要根据具体情况做优化处理。
转载自:https://juejin.cn/post/7223033407037866041