Vue 2 和 Vue 3 响应式使用的差异

动态添加/删除属性

由于 Vue 2 基于Object.defineProperty来实现响应式的局限性,Vue 2 里如下的操作不会触发视图更新:

  • 为对象(数组)添加/删除属性(元素)
  • 对数组基于下标的修改、对于length修改的监测
  • 对 Map、Set、WeakMap 和 WeakSet 的支持

因此,Vue 2 里使用Vue.setvm.$set)和Vue.deletevm.$delete)来解决前两个问题。

vm.$set为数组添加新元素为例:

export default {
  data () {
    return {
      array: []
    }
  },
  mounted () {
    this.$watch(
      () => {
        console.log('re-calculate')
        return this.array[0]
      },
      () => console.log('array[0] 改变了')
    )

    let i = 0
    setInterval(() => {
      console.log(`2000ms 后,设置 array[${i}] = 0`)
      this.$set(this.array, i, 0)
      i++
    }, 2000)
  }
}
// 结果
// re-calculate
// 2000ms 后,设置 array[0] = 0
// re-calculate
// array[0] 改变了
// 2000ms 后,设置 array[1] = 0
// re-calculate
// 2000ms 后,设置 array[2] = 0
// re-calculate
// 2000ms 后,设置 array[3] = 0
// re-calculate
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

可以看到,每当为array添加一个新的元素时,就会打印一次re-calculate,因为调用vm.$set为数组设置新元素会通知所有依赖array的 Watcher 进行重新计算。具体源码及分析可以查看Vue 2.x 响应式原理 - 通知更新 - 引用类型open in new window

但是在 Vue 3 里,基于 Proxy 实现的响应式不再有这些限制,当为数组添加新元素时,不会再通知依赖array的 Watcher 进行重新计算,因为每次只是为array添加新元素,而没有修改array的引用。(因为监听的是array[0],只在array[0]变化时才会触发 Watcher 重新计算)

import { reactive } from '@vue/reactivity'
import { watch } from 'vue';
export default {
    name: 'App',
    setup() {
      const data = reactive({
        array: []
      })

      watch(
        () => {
          console.log('re-calculate')
          return data.array[0]
        },
        () => console.log('data.array[0] 改变了')
      )

      let i = 0
      setInterval(() => {
        console.log(`2000ms 后,设置 data.array[${i}] = 0`)
        data.array[i] = '0'
        i++
      }, 2000)
    }
}
// 结果
// re-calculate
// 2000ms 后,设置 data.array[0] = 0
// re-calculate
// data.array[0] 改变了
// 2000ms 后,设置 data.array[1] = 0
// 2000ms 后,设置 data.array[2] = 0
// 2000ms 后,设置 data.array[3] = 0
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34