loading

Vue - ref 函数和 reactive 函数

第一个区别:按照规范中使用

我们可以使用 reactive 函数创建一个响应式对象或数组。下面是官方的伪代码:

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, value) {
      target[key] = value
      trigger(target, key)
    }
  })
}

在 JavaScript 中有两种劫持 property 访问的方式:getter / settersProxies。Vue 2 使用 getter / setters 完全是出于支持旧版本浏览器的限制。而在 Vue 3 中则使用了 Proxy 来创建响应式对象,仅将 getter / setter 用于 ref。

war:[start] reactive 创建响应式对象或数组,基础类型不能被创建。查看 Vue 中的响应性是如何工作的 一节中的伪代码就可以知道为什么。war:[end]

对象或数组通过 new Proxy 来代理对象的 getter 和 setter 追踪数据的变化,从而反应到视图层,实现了响应式。从这里可以发现,reactive 不可以创建基础类型,比如,数字、布尔值。

下面是 ref 函数的伪代码:

function ref(value) {
  const refObject = {
    get value() {
      track(refObject, 'value')
      return value
    },
    set value(newValue) {
      value = newValue
      trigger(refObject, 'value')
    }
  }
  return refObject
}

反过来,ref 可以创建 reactive 可以创建的类型。但实际上,ref 在遇到一个对象下面有嵌套对象时,会调用 reactive 来创建代理对象,而这就是为什么,由 ref 函数创建的嵌套对象不需要 .value 来得到数据。这就是,深层次响应式与浅层响应式的知识点

第二个区别:reactive 丢失响应式

由上面的分析可知,reactive 返回的是一个代理对象,如果源对象被替换掉了,指向的对象就发生了变化,那么响应式就失效了。比如下面,我们将 state 这个变量直接替换掉,比如 state = { count: 0, size: 100 },就会导致 state 失去响应式,就如下图所示中那样,无论我怎么点击第一个按钮都没有效果。

let state = reactive({ count: 0 });

图1 - 替换 reactive 创建的响应式对象失去响应式功能

但是如果由 ref 函数创建的响应式对象,就不会发生这种情况,仔细观察上面给的伪代码,如果替换掉,就重新添加了一个追踪器,没有 new 的过程,这就是每次替换都会有响应式效果。

let state = ref({ count: 1 });

图2 - 替换 ref 创建的响应式对象没有失去响应式功能

第三个区别:ref 遇到对象时

ref 遇到一个对象类型的数据时,会利用 reactive 进行创建。这就是我前面提到的深层次响应式和浅层响应式

Vue3 文档中有一句话:“如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包”。这句话非常难懂,也可能就作者能第一时间理解是个什么意思了。

const obj = {
  foo: {
    bar: 1
  }
}

const state = ref(obj);

state.value.foo.bar; // => 1

对这句话,以我的理解:

如果有一个对象是嵌套的,即一层以上的对象。强行给 ref 传递这个嵌套对象,第一层是 ref 创建,第二层通过 reactive 创建,还有很多层就递归地通过 reactive 继续创建,所以是深层的。

所以,我们访问第一层的对象需要通过 .value 访问到,但是,访问第二层以及之后的嵌套对象都不需要了。

posted @ 2023-03-19 01:09  Himmelbleu  阅读(11)  评论(0编辑  收藏  举报