深入浅出 solid.js 源码 (五)—— 从 render 开始
这一篇开始正式进入源码中,和 react 很相似,solid.js 逐渐最终也是需要挂载在一个 DOM 节点上,我们调用的是 render 方法:
render(<App>, document.getElementById('app'))
我们就从 render 来入手,开始 solid 的学习。
先来看一下 render 方法是什么,solid 源码中大量使用了 export * from xxx 的写法,这样的好处是方便导入,但是对于源码阅读的角度来看就不够友好了。现在可以确定的是 render 位于 web 目录下,我们可以尝试在 web 目录中搜索 render 方法,不过很遗憾,搜不到。在 web 目录中有限的内容里,唯一有可能导出 render 的就是这一行:
export * from "dom-expressions/src/client";
这里导出的是 dom-expressions 中的内容,这是一个独立的 npm 包,我们可以去查看它的主仓库。
dom-expressions 严格来说也是 solid.js 的一部分,他也是由 solid 作者开发,内部封装了 dom 相关的转化逻辑,它和 solid 的关系类似于 react 和 react-dom,不过只是类似,实现原理上是不同的。react 是使用虚拟 dom 实现的渲染,react-dom 是实现了浏览器上的虚拟 dom 渲染能力。而 solid 完全是通过编译的手段完成的工作,因此并没有虚拟 dom 层,它是直接把原始逻辑编译成 dom 操作,而 dom-expressions 库提供的就是与此相关的能力:babel-plugin-jsx-dom-expressions 是一个 babel 编译插件,他实现了把 solid 逻辑转化为 dom 操作方法的功能,而这些 dom 的操作具体的实现,就是由 dom-expressions 来实现的,这样就通过编译的手段实现了 solid 组件的渲染能力。
下面以我们现在的 render 为例来看下它是如何实现的。首先我们去 dom-expressions 中搜索 render:
export function render(code, element, init) {
let disposer;
root(dispose => {
disposer = dispose;
element === document
? code()
: insert(element, code(), element.firstChild ? null : undefined, init);
});
return () => {
disposer();
element.textContent = "";
};
}
这里的 root 引自 rxcore,rxcore 并不是一个真实的包名,在 readme 文件中有介绍,它只是用来占位,需要使用 babel-plugin-transform-rename-import 插件来将其替换为实际路径。我们可以查看 solid 库的 babel 配置:
plugins: [
[
"babel-plugin-transform-rename-import",
{
replacements: [
{
original: "rxcore",
replacement: "../../../packages/solid/web/src/core"
},
{
original: "^solid-js$",
replacement: "../../src",
}
]
}
],
可以看到在这里的 rxcore 实际上就是 web 下的 core,其中的 root 就是引自 solid-js 的 createRoot 方法,它位于 reactive 目录中 signal 文件下。
这里可以看到,我们的传入的组件即 code 参数是一个可调用的函数,并且在这里会被调用到,接下来我们来看一下组件是如何被处理和转化的。
转载自:https://juejin.cn/post/7126223499454054436