vue3 的 reactive 在创建时的一些小细节
reactive 内部有个“缓存池”
上午写组件的 props 不让改的时候,写着写着弄跑题了,因为懒没有使用父组件传递的方式,而是直接用了 props 的默认值,结果打印结构的时候发现,和自己组建的结构不一致。
这不行呀,结构对不上要怎么证明自己的想法?于是脑一抽用 toRaw 取了原型(和结构一致了)。 但是取原型还能有响应性吗?好奇试了一下,居然还真有!
这是什么原理?于是开启钻牛角尖模式,设置断点跟踪内部代码。 一开始想的是,一个对象(地址相同的),可以对应多个 reactive,改一个另一个也跟着变了。但是,跟踪发现,只对应一个 reactive。
那么是怎么响应的?于是又跟踪创建环节,终于发现:原来 reactive 内部用 Map 创建了一个集合,用对象作为 key,存放了运行后创建的所有 reactive。
当再次创建 reactive 的时候,会根据对象到集合里面寻找,如果找到了,那么直接返回集合里面的 reactive,不会创建新的 reactive。
- 如图所示,到ProxyMap里面寻找,如果找到了,直接返回 existingProxy。
这样的话,表面上看是两个 reactive,其实是同一个。这样就可以解释,为啥改一个另一个也跟着响应了。
验证一下
const foo1 = reactive({
name: '测试一下'
})
const foo2 = reactive({
name: '测试一下'
})
const foo3 = reactive(toRaw(foo1))
console.log('对比 foo1 foo2', foo1 === foo2) // 不等
console.log('对比 foo1 foo3', foo1 === foo3) // 相等
- 对比结果
对比 foo1 foo2: false
对比 foo1 foo3: true
表面上看 foo1 和 foo2 是一样的,但是对象的地址并不一致,所以是不等的。
foo3 使用 foo1 的原型创建,因为地址相同,所以后者并没有重新创建,而是从缓存池里获取,可以说是同一个 reactive。
这也是定义状态的时候,需要使用函数;给 props 里对象类型的属性,设置默认值的时候,也需要使用函数的原因。
调用函数可以返回的对象其地址是不同的,才会有不同的状态、 props。
另外发现 使用 withDefaults + defineProps + type 定义 props ,会编译成运行时校验的方式(暂时似乎不支持 validator 方式),这样 ts 不仅在编写时有提示,在运行时也可以有效(通过vue实现,限 props)。
这样定义 props 的时候,就不用纠结了,直接 ts, 既好用,功能也不缺失。
转载自:https://juejin.cn/post/7240623529518481464