Vue3 和 Vue2 的响应式哪个性能更好?

lxf2023-05-22 01:29:58

面试题处处有坑,让人防不胜防,不提前刷下面试题大概率会被面试官问的一脸闷逼。

面试官问: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

数组长度初始化时间内存
11ms1.5MB
10006ms2.4MB
100000162ms57MB
10000001443ms569MB

从表中可以看到随着数组的长度增加,初始化时间和内存使用量有明细的增加。

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");

性能:

数组长度初始化时间内存
14ms1.2MB
10003ms2.5MB
1000004ms6.8MB
10000004ms25MB

数组长度增加初始化时间几乎没什么变化,内存使用相对 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>

性能:

数组长度更新时间
11ms
10004ms
100000304ms
10000003293ms

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>

性能:

数组长度更新时间
15ms
100075ms
1000006708ms
100000070980ms

小结

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>

性能:

数组长度更新时间
12ms
10001ms
1000002ms
10000004ms

可以看到 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");

性能:

数组长度更新时间
11ms
10005ms
100000164ms
10000001828ms

将 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>

性能:

数组长度更新时间
11.2ms
10002ms
1000007ms
100000045ms

这种方式更新时间更快,但是这种只适用于列表展示,修改都要重新赋值。

总结

Vue3 的响应式性能并不是完全比 Vue2 的好,在对数组响应式处理上要根据具体情况做优化处理。

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!