深入学习Vue.js(五)实现ref函数

1.为什么要有ref

在JavaScript中,Proxy无法提供对原始值的代理,因此想要将原始值变成响应式数据,就必须对其做一层包裹,也就是ref。

   对于一个原始值来说,我们没有办法拦截对它的任何操作,但是我们可以使用一个非原始值,去包裹一个原始值。

const wrapper = {
    value: 'name'
}
const name = reactive(wrapper);
name.value; // name
// 修改值可以触发响应
name.value = 'volit'; 

   为了保证规范性,我们将上面的代码段封装为ref函数。

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

  return reactive(wrapper);
}

   现在,我们就可以使用ref函数创建原始值的响应变量了。

const name = ref("");
effect(() => {
  console.log(name.value);
});

name.value = "volit"; // volit

   为了让ref拥有自动脱ref的能力,我们还需要给ref对象设置一个专门的标记,用来识别对象是否是ref对象。

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

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

  return reactive(wrapper);
}

2.响应丢失

​ 通常我们可能会对一个reactive对象进行展开操作。

export default {
    setup() {
        const obj = reactive({foo: 1, bar: 2});
        
        return {
            ...obj
        }
    }
}

   然后在模板中进行访问:

<templete>
	<p>
        {{for}} / {{bar}}
    </p>
</templete>

   但是此时你会发现,obj对象的响应式特性丢失了,当我们修改obj的属性值的时候,并没有触发响应式操作。这是因为,展开运算符在在进行展开的时候,会把属性单独拆分出来,对于一个原始值属性,自然就丢失了响应式特性,对此,我们封装toRef和toRefs函数,对丢失响应特性的属性进行封装。

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

  return wrapper;
}

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

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

   同时为了和ref保持概念上的统一,我们同样对toRef创建出来的对象加入ref标记。

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

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

  return wrapper;
}

   由于toRef函数只设置了get访问器,因此只能创建只读属性,对此我们给其加上set访问器。

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;
}

3.自动脱ref

​ 虽然toRef函数解决了响应丢失的问题,但随即带了了增加用户心智负担的问题,因为对于每一个ref对象,我都要通过.value来获取它的值,同时我们在模板中,使用ref变量的值的时候,同样也不希望使用.value去访问变量的值,这时我们就可以使用到前面提到过的__v_isRef标记了,通过封装一个proxyRefs函数,判断属性是否拥有ref对象标记,来赋予属性自动脱ref的能力。同时,不仅在模板中具有自动脱ref的功能,reactive同样具有自动脱ref的能力。

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, newVal, receiver) {
      const value = target[key];
      if (value.__v_isRef) {
        value.value = newVal;
        return true;
      }
      return Reflect.set(target, key, newVal, receiver);
    },
  });
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

volit_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值