前言
我研究 Solid.js
源码已经有一段时间了,在钻研的过程中我发现了其中的一些迷惑行为,在搞懂之后终于恍然大悟,忍不住想要分享给大家。不过这么说其实也不太准确,因为在严格意义上来讲 Solid.js
其实是被划分为了两个部分的。我只认真钻研了其中一个部分,所以也不能说钻研 Solid.js
源码,因为另外一个部分压根就不叫 Solid
。
有些同学看到这可能就会感到疑惑了,哪两个部分?Solid
、.js
?其实是这样:大家应该都听说过 Solid.js
是一个重编译、轻运行的框架吧,所以它可以被分为编译器和运行时两个部分。那有人可能会问:你要是这么说的话那岂不是 Vue
也可以被分为两部分,毕竟 Vue
也有编译器和运行时,为什么从来没有人说过 Vue
是两部分组成的呢?是这样,Vue
的编译器和运行时全都放在了同一仓库内的 Monorepo
中:
你可以说 Vue2
和 Vue3
是两个部分,因为它俩被放在了两个不同的仓库中:
虽然它俩已经是两个不同的仓库了,但好歹也都是 vuejs
名下的吧:
而 Solid.js
的两部分不仅不在同一个仓库内,甚至连组织名都不一样:
一个是 solidjs/solid:
而另一个则是 ryansolid/dom-expressions:
ryan
是 Solid.js
作者的名字,所以 ryan
+ solid
= ryansolid
(有点迷,为啥不放在 solidjs
旗下非要单独开一个 ryansolid
)
这个 dom-expressions
就是 Solid.js
的编译器,那为啥不像 Vue
编译器似的都放在同一个仓库内呢?因为 Vue
的编译器就是专门为 Vue
设计的,你啥时候看非 Vue
项目中使用 xxx.vue
这样的写法过?.vue
这种单文件组件就只有 Vue
使用,虽说其他框架也有单文件组件的概念并且有着类似的写法(如:xxx.svelte
)但人家 Svelte
也不会去用 Vue
的编译器去编译人家的 Svelte
组件。不过 Solid
不一样,Solid
没自创一个 xxx.solid
,而是明智的选择了 xxx.jsx
。
单文件组件和 jsx
各有利弊,不能说哪一方就一定比另一方更好。但对于一个声明式框架作者而言,选择单文件组件的好处是可以自定义各种语法,并且还可以牺牲一定的灵活性来换取更优的编译策略。缺点就是成本太高了,单单语法高亮和 TS
支持度这一方面就得写一个非常复杂的插件才能填平。好在 Vue
的单文件组件插件 Volar
已经可以支持自定义自己的单文件组件插件了,这有效的降低了框架作者的开发成本。但 Solid
刚开始的时候还没有 Volar
呢(可以去看看 Volar
的源码有多复杂 这还仅仅只是一个插件就需要花费那么多时间和精力),甚至直到现在 Volar
也没个文档,就只有 Vue
那帮人在用 Volar
(毕竟是他们自己研究的):
并且人家选择 jsx
也有可能并非是为了降低开发成本,而是单纯的钟意于 jsx
语法而已。那么为什么选择 jsx
会降低开发成本呢?首先就是不用自己写 parser
、generator
等一堆编译相关的东西了,一个 babel
插件就能识别 jsx
语法。语法高亮、TS
支持度这方面更是不用操心,甚至用户都不需要为编辑器安装任何插件(何时听过 jsx
插件)。并且由于 React
是全球占有率最高的框架,jsx
已被广泛接受(甚至连 Vue
都支持 jsx
)但如果选择单文件组件的话又会产生有人喜欢这种写法有人喜欢那种写法的问题,比方说同样使用 sfc
的 Vue
和 Svelte
,if-else
写法分别是这样:
<!-- Vue -->
<template>
<h1 v-if="xxx" />
<div v-else />
</template>
<!-- Svelte -->
{#if xxx}
<h1 />
{:else}
<div />
{/if}
有人喜欢上面那种写法就有人喜欢下面那种写法,众口难调,无论选择哪种写法可能都会导致另一部分的用户失望。而 jsx
就灵活的多了,if-else
想写成什么样都可以根据自己的喜好来:
if (xxx) {
return <h1 />
} else {
return <div />
}
// 或者
return xxx ? <h1 /> : <div />
// 亦或
let Title = 'h1'
if (xxx) Title = 'div'
return <Title />
jsx
最大程度的融合了 js
,正是因为它对 js
良好的兼容性才导致它的适用范围更广,而不是像 Vue
、Svelte
那样只适用于自己的框架。毕竟每种模板语言的 if-else
、循环等功能写法都不太一样,当然 jsx
里的 if-else
也可以有各种千奇百怪的写法,但毕竟还是 js
写法,而不是自创的 ng-if
、v-else
、{:else if}
{% for i in xxx %}
等各种不互通的写法。
正是由于 jsx
的这个优势导致了很多非 React
框架(如:Preact、Stancil、Solid 等)用 jsx
也照样用的飞起,那么既然 jsx
可以不跟 React
绑定,那 Ryan
自创的 jsx
编译策略也同样可以不跟 Solid
绑定啊对不对?这是一款可以和 Solid.js
搭配使用的 babel
插件,也同样是一款可以和 MobX
、和 Knockout
、和 S.js
、甚至和 Rx.js
搭配使用的插件,只要你有一款响应式系统,那么 dom-expressions
就可以为你提供 jsx
服务。
所以这才是 Ryan
没把 dom-expressions
放在 solidjs/solid 里的重要原因之一,但 Solid.js
又是一个注重编译的框架,没了 dom-expressions
还不行,所以只能说 Solid.js
是由两部分组成的。
DOM Expressions
DOM Expressions
翻译过来就是 DOM
表达式的意思,有人可能会问那你标题为啥不写成《盘点 DOM Expressions 源码中的那些迷惑行为》
?拜托!谁知道 DOM Expressions
到底是个什么鬼!如果不是我苦口婆心的说了这么多,有几个能知道这玩意就是 Solid.js
的编译器,甭说国内了,就连国外都没几个知道 DOM Expressions
的。你要说 Solid.js
那别人可能会竖起大拇指说声 Excellent
,但你要说 DOM Expressions
那别人说的很可能就是 What the fuck is that?
了。不信你看它俩的🌟对比:
再来看看 Ryan
在油管上亲自直播 DOM Expressions时的惨淡数据:
这都没我在掘金随便写篇文章的点赞量高,信不信如果我把标题中的 Solid.js
换成了 DOM Expression
的话点赞量都不会有 Ryan
直播的数据好?好歹人家还是 Solid
的作者,都只能获得如此惨淡的数据,那更别提我了。
言归正传,为了防止大家不知道 Solid.js
编译后的产物与 React
编译后的产物有何不同,我们先来写一段简单的 jsx
:
import c from 'c'
im