第六章 原始值的响应式实现

69 阅读1分钟

原始值的响应式实现

  • Proxy 无法提供对原始值的代理需要使用一层对象作为包裹,间接实现原始值的响应式方案。由于“包裹对象”本质上与普通对象没有任何区别,因此为了区分 ref 与普通响应式对象,我们还为“包裹对象”定义了一个值为 true 的属性生,即 __v_isRef,用它作为 ref 的标识。

  • ref 除了能够用于原始值的响应式方案之外,还能用来解决响应丢失问题。为了解决该问题,我们实现了 toRef 以及 toRefs 这两个函数。它们本质上是对响应式数据做了一层包装,或者叫作“访问代理”。

function ref(val) {
  const wrapper = {
    value: val,
  };

  Object.defineProperty(wrapper, "__v_isRef", {
    value: true,
  });

  return reactive(wrapper);
}

const obj = reactive({ foo: 1, bar: 2 });

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

function toRef(obj, key) {
  const wrapper = {
    get value() {
      return obj[key];
    },
    set value(val) {
      obj[key] = val;
    },
  };

  Object.defineProperty(wrapper, "__v_isRef", {
    value: true,
  });

  return wrapper;
}

function proxyRefs(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const value = Reflect.get(target, key, receiver);
      return value.__v_isRef ? value.value : value;
    },
    set(target, key, newValue, receiver) {
      const value = target[key];
      if (value.__v_isRef) {
        value.value = newValue;
        return true;
      }
      return Reflect.set(target, key, newValue, receiver);
    },
  });
}

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