vue3响应式实现原理(5)

93 阅读1分钟

简单数据类型的代理

简单数据类型需要使用ref,实际上ref的背后还是reactive.本质是手动给他加一层,并且给他一个ref的标记__v_isRef

function ref(val) {
  const wrapper = {
    value: val,
  };
  // 使用 Object.defineProperty 在 wrapper 对象上定义一个不可枚举的属性 __v_isRef,并且值为 true
  Object.defineProperty(wrapper, "__v_isRef", {
    value: true,
  });
  return reactive(wrapper);
}
let proxyNum = ref(1);
effect(() => {
  console.log(proxyNum.value);
});
proxyNum.value++;

setup暴露出的响应式对象,经过...会失去响应式,首先来模拟一下这种情景

let newObj = { ...proxyData }
effect(() => {
 console.log(newObj.age)
});
newObj.age++

问题关键在于有没有办法能够帮助我们实现:在副作用函数内,即使通过普通对象 newObj 来访问属性值,也能够建立响应联系,方法是有的,可以借助访问器,同时要有get和set.如果没有set不能修改数据

newObj = {
  age: {
    get value() {
      return proxyData.age;
    },
    set value(val) {
      proxyData["age"] = val;
    },
  },
};
effect(() => {
  console.log(newObj.age.value);
});

newObj.age.value++;

可以将上面的逻辑封装成一个函数,并添加__v_isRef

function toRef(proxyData, key) {
  let wrapper = {
    get value() {
      return proxyData[key];
    },
    set value(val) {
      proxyData[key] = val;
    },
  };
  Object.defineProperty(wrapper, "__v_isRef", {
    value: true,
  });

  return wrapper;
}

也可以一次性完成对象的操作,这样newObj修改他的值也会有触发响应

function toRefs(obj) {
  const ret = {};
  for (const key in obj) {
    ret[key] = toRef(obj, key);
  }
  return ret;
}

const newObj = { ...toRefs(obj) }

我们都知道ref类型在模板是不需要使用.value的,这其实要借助我们之前的标__v_isRef,在get中判断是否是ref返回不同的数据

return value.__v_isRef ? value.value : value

至此响应系统的内容结束