为何 Vue3 的 ref 让不少大佬操碎了心?

做者:瑞哥
来源:瑞哥聊前端

最近 Vue3 关于 ref-sugar 的提案引发了普遍的讨论:juejin.im/post/689417…[1]前端

<script setup>
import Foo from './Foo.vue'

// declaring a variable that compiles to a ref
ref: count = 1
const inc = () => {
  count++
}
// access the raw ref object by prefixing with $
console.log($count.value)
</script>

<template>
  <Foo :count="count" @click="inc" /
>
</template>

感兴趣的同窗能够先阅读上面的讨论,本文再也不重复讨论。提案目的是将 ref.value 的写法进一步简化,但由于修改了 js 语言自己的语义,引发了不少的争论。vue

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

1. 回顾 vue-composition-api-rfc

读写 ref 的操做比普通值的更冗余,由于须要访问 .value。尤大大对使用编译时的语法糖来解决这个问题很是谨慎,曾明确表示不默认提供此类支持。vue-composition-api-rfc.netlify.app/zh/#\%E5\%BC\%…[2]git

对此我很是赞同。github

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

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 函数

// 输出结果

// 输出 proxy instanceof String:true
// 输出 proxy.valueOf():foo
// 输出:proxy.substring(1): oo
// ==========
// 输出 proxy instanceof String:true
// 输出 proxy.valueOf():bar
// 输出:proxy.substring(1): ar

  • 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. 总结

  • 取值用.valueOf() 。开发者须要时刻注意 proxy 是 String, Number, Boolean 对象,记得调用 .valueOf()方法才能取到实际值。配合 eslint 插件可帮助检查漏写.valueOf()方法
  • 修改用.setValue() 。这种解决方案完整保留 js 语言特性,仅仅只是增长了 .setValue() 方法,对开发者的心智负担可能略少于 ref
  • 实现源码仓库:https://github.com/ruige24601/re-primitive.git

参考资料

[1]

juejin.im/post/689417…: https://juejin.im/post/6894175515515551752算法

[2]

vue-composition-api-rfc.netlify.app/zh/#%E5%BC%…: https://vue-composition-api-rfc.netlify.app/zh/#%E5%BC%95%E5%85%A5-ref-%E7%9A%84%E5%BF%83%E6%99%BA%E8%B4%9F%E6%8B%85api

最后

欢迎关注「前端瓶子君」,回复「 交流 」加入前端交流群!
欢迎关注「前端瓶子君」,回复「 算法 」自动加入,从0到1构建完整的数据结构与算法体系!
另外,每周还有手写源码题,瓶子君也会解答哟!
》》面试官也在看的算法资料《《
“在看和转发” 就是最大的支持

本文分享自微信公众号 - 前端瓶子君(pinzi_com)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。微信

相关文章
相关标签/搜索