Vue3 中利用 reactive
与 ref
两个 API 声明创建响应式数据. 感觉这个设计不是很理想.
reactive
利用 Proxy 实现, 但无法跟踪原始值. 故提出 ref
, 其本质上是创建一个对象, 对原始值进行封装, 当然也可以承接对象, 内部仍然依赖 reactive
实现. ref
比较全能, 但弊端是需要通过 .value
来取赋值.
reactive
与 ref
到底用谁? 官方的意思是尽量使用 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 对象.