深入浅出 solid.js 源码 (六)—— 组件编译
上一篇我们看了 solid 的 render,这一节来看 jsx 组件的处理。熟悉 react 的开发者对 jsx 一定不陌生,在 react 中,jsx 会被编译为 createElement,这个方法可以创建虚拟 dom 对象。在 solid.js 中也有一套特有的 jsx 编译体系,接下来就看一下 solid.js 中的 jsx 编译。
查看 solid.js 编译结果最简单的方式是使用在线版本的 solid playground,访问 Solid Playground (solidjs.com) ,在左侧编写 solid.js 程序在右侧就会实时生成编译结果,我们继续以计数器为例:
import { render } from "solid-js/web";
import { createSignal } from "solid-js";
function Count() {
const [num, setNum] = createSignal(0);
return (
<div>
{num()}
<div onClick={() => setNum((p) => p + 1)}>+</div>
<div onClick={() => setNum(0)}>reset</div>
<div onClick={() => setNum((p) => p - 1)}>-</div>
</div>
);
}
render(() => <Count />, document.getElementById("app")!);
这是原始的 solid.js 版本,放在 playground 中,我们就可以得到编译后的结果:
import { render, createComponent, delegateEvents, insert, template } from 'solid-js/web';
import { createSignal } from 'solid-js';
const _tmpl$ = /*#__PURE__*/template(`<div><div>+</div><div>reset</div><div>-</div></div>`, 8);
function Count() {
const [num, setNum] = createSignal(0);
return (() => {
const _el$ = _tmpl$.cloneNode(true),
_el$2 = _el$.firstChild,
_el$3 = _el$2.nextSibling,
_el$4 = _el$3.nextSibling;
insert(_el$, num, _el$2);
_el$2.$$click = () => setNum(p => p + 1);
_el$3.$$click = () => setNum(0);
_el$4.$$click = () => setNum(p => p - 1);
return _el$;
})();
}
render(() => createComponent(Count, {}), document.getElementById("app"));
delegateEvents(["click"]);
这里的 jsx 被编译成了 createComponent,这个函数位于 component.ts 文件中,除去 ssr 的逻辑,有效代码是这么多:
export function createComponent<T>(Comp: Component<T>, props: T): JSX.Element {
if ("_SOLID_DEV_") return devComponent(Comp, props || ({} as T));
return untrack(() => Comp(props || ({} as T)));
}
无论是否为 dev 模式,我们传入的 Comp 都是作为一个函数来执行的,其中 props 会以参数形式传入。因此在这个过程中,我们只需要把组件逻辑理解为一个函数本身即可。
我们可以看到,在编译后的版本中,createSignal 还保持原状,这个是 solid 运行时的逻辑,用于实现 solid 的响应式更新能力。此外 dom 的创建,事件绑定都被转化为一些内部函数调用,这些函数都是比较基础的,下面直接对应了原生的 dom 操作,也就是说在这里调用的已经是对原生 dom 的操作代码了,没有虚拟 dom 层,一切都非常直接。
这里的 dom 节点是通过 template 来创建的,这些方法都位于 dom-expressions 中,这里的 template 实际上底层就是利用了浏览器中的 template 标签,这是浏览器的原生 API,接触过 WebComponent 对此应该会很熟悉。
在 dom-expressions 中还封装了很多 dom 操作的实现,除了 template 外,上面的 insert、delegateEvents 也都是此类方法,这部分逻辑比较简单,在后面的章节中会深入阅读 dom-expressions 中的逻辑。
转载自:https://juejin.cn/post/7126584531322142756