最近 Vue3 关于 ref-sugar 的提案引起了广泛的讨论: juejin.im/post/689417…
<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 语言本身的语义,引起了很多的争论。
本文希望用一种最保守的思路提供另一种心智负担可能更小的解决方案
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,使用示例如下:
js
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 三种类型,可简写去掉主动装箱。
js
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
作者:瑞哥24601
链接:https://juejin.im/post/6895213945997492238
看完两件小事
如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:
- 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
- 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程
本文著作权归作者所有,如若转载,请注明出处
转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com