Vue3 的 ref 真让很多大佬操碎了心

时间:2021-1-18 作者:admin

最近 Vue3 关于 ref-sugar 的提案引起了广泛的讨论:
juejin.im/post/689417…

感兴趣的同学可以先阅读上面的讨论,本文不再重复讨论。

提案目的是将 ref.value 的写法进一步简化,但因为修改了 js 语言本身的语义,引起了很多的争论。

本文希望用一种最保守的思路提供另一种心智负担可能更小的解决方案

1. 回顾 vue-composition-api-rfc

读写 ref 的操作比普通值的更冗余,因为需要访问 .value。尤大大对使用编译时的语法糖来解决这个问题非常谨慎,曾明确表示不默认提供此类支持。
vue-composition-api-rfc.netlify.app/zh/#%E5%BC%…

对此我非常赞同。

但在 js 中必须要加 .value ,在模板中又不需要加 .value,这无疑造成了一定程度的混乱和割裂感。

2. 解决思路:转成对象 new String(‘foo’) 再拦截

为什么会这样?究其根本,我们无法对基础数据类型值做拦截。行呀,那我们把基础数据类型转成对应的包装类实例,再进行拦截。
例如: let str = 'foo' 改写成 let strObj = new String('foo'),此时 strObj 是对象,接下来我们尝试拦截,我写了一个库re-primitive,使用示例如下:

const { rePrimitive, watchEffect } = require('../dist/re-primitive.cjs')

// 用 rePrimitive 代替 ref; 并传入String的包装对象  new String('foo')
let proxy = rePrimitive(new String('foo'))
// let proxy = rePrimitive('foo')  // 内部做了装箱,可简写去掉 new String()
// rePrimitive 的作用是将对象设置成响应式,并增加 setValue() 修改数据的方法

watchEffect(() => {
  console.log('输出 proxy instanceof String:', proxy instanceof String) // true , 可以看出 proxy是String的实例,可以使用所有的String的原型方法
  console.log('输出 proxy.valueOf():' + proxy.valueOf())
  console.log('输出:proxy.substring(1): ' + proxy.substring(1))
})
console.log('==========')
proxy.setValue('bar') // 响应式修改数据,重新执行 effect 函数
  • rePrimitive: 相当于 reactive, ref。将基本数据类型设置成响应式,如果传递的是 new String(‘foo’), 则 proxy 仍然是 String 的实例;
    支持 String | Number | Boolean 三种类型,可简写去掉主动装箱。
let proxyStr = rePrimitive('foo') // 等价于 let proxyStr = rePrimitive(new String('foo'))
let proxyNum = rePrimitive(123) // 等价于 let proxyNum = rePrimitive(new Number(123))
let proxyBool = rePrimitive(false) // 等价于 let proxyBool = rePrimitive(new Boolean(false))
  • setValue: 响应式修改数据,重新执行 effect 函数

3. 总结

  • 开发者需要时刻注意 proxy 是对象,记得调用 .valueOf()方法才能取到实际值。配合 eslint 插件可帮助检查漏写.valueOf()方法
  • 这种解决方案完整保留 js 语言特性,仅仅只是增加了 setValue() 方法,对开发者的心智负担可能略少于 ref
  • 实现源码仓库:https://github.com/ruige24601/re-primitive.git
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。