这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
前面一直在看运行时部分的逻辑,solid 的运行时逻辑比较轻量,很多工作是在编译阶段完成的,这一节我们来看编译的工作。
solid 编译阶段是使用 babel-preset-solid 来处理的,它的源码也在 solid 仓库,是一个独立的包。不过这个包里面并没有做其他工作,只是单纯地为 babel 添加了一个插件 babel-plugin-jsx-dom-expressions。
babel-plugin-jsx-dom-expressions 也是 dom-expressions 库的一部分,它实现了 solid 的 jsx 编译工作。这个库最终是会以 babel 插件的形式进行引入的。有关 babel 插件的详细介绍可以参阅 babel handbook,本文只会提及一些涉及到的基础概念。
babel 插件的入口是 index.js,其中最重要的是 visitor,visitor 定义了 babel 对不同类型节点的处理操作。babel AST 中每一个节点都有一个 type 属性,对于某种特定的 type 类型,我们可以自定义其转化处理的逻辑:
visitor: {
JSXElement: transformJSX,
JSXFragment: transformJSX,
Program: {
enter: preprocess,
exit: postprocess
}
}
visitor 中每个函数有 path 和 state 两个参数,这里比较重要的就是 transformJSX,我们来看它的实现:
export function transformJSX(path) {
const config = getConfig(path);
const replace = transformThis(path);
const result = transformNode(
path,
t.isJSXFragment(path.node)
? {}
: {
topLevel: true
}
);
const template = getCreateTemplate(config, path, result);
path.replaceWith(replace(template(path, result, false)));
}
这里面就是一系列转化逻辑了,这部分具体的实现就是一些 AST 的操作,详细的过程就不展开了,这里我们需要重点关注的还是转化结果。前面我们已经见到了转化的结果了,可以回顾一下之前在 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 结构处理,其中需要渲染为 dom 的部分会调用 template,这里的效果是创建一个 html template 标签并添加内容,template 标签可以通过 cloneNode 的方式创建节点。
如果页面中引用到 signal 值,这里会通过 dom API 动态添加,这里的 num 是通过 insert 来插入的,底层实现的是 html 插入逻辑。
比较有意思的是事件处理,这里的事件是通过设置 $$xxx 方法来添加的,最终会调用 delegateEvents 来统一把这些函数转化为标准的事件处理函数。
当然目前的应用非常简洁,还没有涉及到 solid 内置的流程控制组件处理,在接下来几篇会继续展开这部分内容。