type
status
date
slug
summary
tags
category
icon
password
Edited
Oct 24, 2024 12:18 AM
Created
Oct 15, 2024 04:50 AM
在前面的文章中,我们学习了 Solid 的响应式原理,深入了了解其实现方式。
这篇文章将主要深入解析组件内部props的原理,为什么结构后会导致响应式丢失?
案例
我们以一个例子作为参考,由浅入深的讲解其中的奥秘。
Parent.tsx
Child.tsx
组件编译
先来看看组件编译后是什么样子的,官网也提供了 Playground,可以进行尝试:
Parent.tsx
组件编译效果
Child.tsx
组件编译效果
编译内容和源码本身其实大部分是一致的,只是对最终的 DOM 实现这块进行了特殊处理;暂时剥去其他的内容先不管,看看
Child
组件被编译成了什么:可以看到,使用了
createComponent
生成组件,同时将 props
转换成一个对象进行传递。createComponent
里实际就是调用了 Comp 函数式组件:先来看一下这个转换后的
props
对象,重点关注一下它创建对象的这种方式,说一下这种方式的好处:- 这种方式直接将数据封装在对象内部,而不是直接暴露,同时,也无法去修改属性值。
- 和 Solid 结合,这样,每次调用
name()
的时候,都能拿到最新的值。
- 延迟计算,只有在访问的时候才会执行。
原因
根据之前文章(响应式原理)那一篇,我们能得知,保持响应式的关键就是这个
name()
、age()
这两个 Signal
,它们内部执行会进行依赖收集操作。那如果把它进行结构,就会直接获取到
name()
的值,后续时候就会丢失 readSignal
的执行,只是单纯的一个值。这也是,为什么不建议拆分
props
进行使用的原因。官方对此也有说明:
如果你想解构下来使用,可以通过以下方式:
当然,这种方式,实际还是调用的
Signal
,这在 Solid 里面有一个术语,叫 Derived Signal
(派生 Signal)。特殊例子
再来看几个特殊的方式,如果我把传递
Signal
的方式改为传递函数呢?这样,你是可以在子组件中解构下来使用的,因为这时你传入的是一个函数了,不再是一个简单的
Signal
值。再把子组件改成如下方式调用。
实际上,也是生效的。
对照一开始那个案例来看,我们知道
props
实现响应式的本质还是 Signal
的处理。所以这里的 Child
并没有违背这个理念,只是换了种方式,而且是可行的。甚至说,子组件不变都是生效的。
这其实和 Vue 使用
ref
是一个道理,在 Vue 中的 template 使用 ref
并不需要手动添加 value
属性,因为 Vue 在编译的时候给你处理了。同理,Solid 也会在编译的时候给 Signal 做处理。
可以回头看一下 Child 组件被编译的结果,会调用一个
insert
函数。来看一下这个函数内部的处理:
dom-expresssions/packages/dom-expresssions/src/client.js
对于函数的情况,Solid 内部会自动做一层响应式处理。
额外
我们知道 Solid 提供了两个方法,来管理/操作
props
的属性。来看看这两个方法里面做了什么,能保持响应式不会丢失的。
mergeProps
以下面这个为例:
我们来先来看一下关于属性合并相关的源码:
主要就是对参数内的对象进行合并,生成一个新的
target
对象。同时,对内部特殊的属性(如
props
上的属性包含get
),做特殊处理,同时每个 key
有一个独立的 sources
,用于做多个同名 key
处理,包括合并多个 props
、默认值等处理。再看一下
resolveSources
做了什么:按上面的案例来说,这里的 this(sources) 数据为
[props.name, () ⇒ name(’default’)]
。因为此时
props.name
为 undefined
,所以会走默认值。这里注意,
props.name
也是执行的,即执行了 Signal
,所以后面 name
更新的时候,这里的数据也会更新。所以说,实际上
mergeProps
只是做了层代理,最终调用的还是 props
上的属性的 get
来实现的响应式。splitProps
同理,来看个案例:
实现上和
mergeProps
的基本类似:根据
keys
对 props
进行拆分,划分到不同的 object
返回即可。回到原文,我们已经知道
props
能保持响应式的原理是什么了,以及为什么会导致其响应式丢失的问题;实际上本质还是 Signal
去实现的。- 作者:JinSo
- 链接:https://jinso365.top/120cee950faf80c996aad3cdf69c7880
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。