Vue3 响应式

27 阅读2分钟

Vue3 中利用 reactiveref 两个 API 声明创建响应式数据. 感觉这个设计不是很理想.

reactive 利用 Proxy 实现, 但无法跟踪原始值. 故提出 ref , 其本质上是创建一个对象, 对原始值进行封装, 当然也可以承接对象, 内部仍然依赖 reactive 实现. ref 比较全能, 但弊端是需要通过 .value 来取赋值.

reactiveref 到底用谁? 官方的意思是尽量使用 ref , 可能是考虑提高工程下限, 弊端是需要大量书写 .value . 另一种方式是对于原始类型采用 ref , 对象采用 reactive . 不论何种方法, 最重要的是团队内统一.

与 Vue2 设计上的区别

Vue2 的早期版本使用 Object.defineProperty 来包装数据获取响应式能力, 也是常说的观察者模式. 这个 API 只能监听组件 data 对象的属性变更, 对于属性内部的变化无能为力, 而且要遍历 data 对象的所有属性, 性能比较弱, 所以在后期 Vue2 也有采用 Proxy 封装 data 对象.

Vue2 的选项式设计导致功能解耦比较差 (代码写不到一个坑里), 故在 Vue3 中增加了组合式设计. 这样的设计下, 响应式数据的声明不再像 Vue2 那样, 只存在于组件的 data 对象. 因此 Vue3 无法单靠 Proxy 实现响应式, 在具体实现上就有了 ref 与 reactive 两个 API .

reactive 替换对象与数组

众所周知, 下面的方式会失去响应式

let data = reactive({ a: 1, b: 2 });
let list = reactive([1, 2, 3, 4])
...
data = reactive({ a: 3, b: 4 })
list = reactive([5, 6, 7, 8])

对于对象可以采用 Object.assign , 对于数组可以使用 splice 方法

Object.assign(data, { a: 3, b: 4 });
list.splice(0, Infinity, ...[5, 6, 7, 8])

ref 作为 reactive 的属性时会自动解包

const data = reactive({
    innerRef: ref(3)
})

上文提过 ref 包装过的值, 需要 .value 来取赋值, 但是这可以直接 data.innerRef , 与正常属性的使用无异. 这个方法可以避免 .value . 所以也会见到一种操作, 利用 reactive 包裹整个页面中需要的数据, 类似 Vue2 中的 data 对象.