🪢Solid 之旅 —— 为什么 props 被解构后会导致响应式丢失
00 分钟
2024-10-24
2024-10-24
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 对象,重点关注一下它创建对象的这种方式,说一下这种方式的好处:
  1. 这种方式直接将数据封装在对象内部,而不是直接暴露,同时,也无法去修改属性值。
  1. 和 Solid 结合,这样,每次调用 name() 的时候,都能拿到最新的值。
  1. 延迟计算,只有在访问的时候才会执行。
 

原因

根据之前文章(响应式原理)那一篇,我们能得知,保持响应式的关键就是这个 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.nameundefined,所以会走默认值。
这里注意,props.name 也是执行的,即执行了 Signal,所以后面 name 更新的时候,这里的数据也会更新。
 
所以说,实际上 mergeProps 只是做了层代理,最终调用的还是 props 上的属性的 get 来实现的响应式。
 

splitProps

同理,来看个案例:
 
实现上和 mergeProps 的基本类似:
根据 keysprops 进行拆分,划分到不同的 object 返回即可。
 
回到原文,我们已经知道 props 能保持响应式的原理是什么了,以及为什么会导致其响应式丢失的问题;实际上本质还是 Signal 去实现的。
 
上一篇
月度摄影 — 2024年10月 ~慢慢拍照,静静生活~
下一篇
百度地图的使用(Mark、Cluster…)

评论
Loading...