在 2021 年的 JavaScript 调查问卷中,黑马 Solid.js 一骑当先登上榜首,在用户满意度的评分上连超两大竞争对手,React 和 Svelte。虽然 React 仍有着不错的市场占有率,但要小瞧了 Solid.js 那可是犯了个大错误。
React 开发者们会发现 Solid 非常眼熟
先看一眼下面这段用 Solid.js 写的简单计数程序:
function Counter() {
const [count, setCount] = createSignal(0);
return (
<div>
<p>The current count is: {count()}</p>
<button onClick={() => setCount((x) => x + 1)}>Plus</button>
</div>
);
}
我敢打包票,即使从来没接触过 Solid.js,React 的开发者们读这段代码也是完全不费力。这是因为 Solid.js 在 API 上和 React 有着异曲同工之妙。其中一个原因是 Solid.js 也使用了 JSX,意味着 React 开发者们刻在 DNA 里关于元素和组件的应用方式也会在 Solid.js 里体现。比如说下面这个“欢迎”组件,我们完全可以以和 React 应用类型的方式来实现:
function Welcome(props) {
return <p>Welcome, {props.name}</p>;
}
这二者之间的相似之处不仅限于此,Solid.js 在图元的使用上也和 React 的 hook 类似,大部分 Solid 响应式图元的 API 你都可以在 React 的 hook 找到对应的存在。
但请注意,Solid.js 可不是 React API 的复制品,React 应用直接放到 Solid.js 中运行是肯定行不通的。但不得不说,有这么个和 React 如此相似的 API 对大部分开发者来说都是件好事,至少学习曲线不会太过陡峭。
一切都是为了图元
尽管这二者在表面上有如此多的相似点,但他们之间的不同之处才是让 Solid.js 得以在众多竞争者中脱颖而出,比如说,Solid.js 的应用并不是围着组件转的。Solid 曾骄傲地称他们的组件为“消失的组件”。在组织代码阶段,Solid 的组件很有帮助,但等到了初始的渲染阶段,你就再也见不到它们了。
与 React 不同,Solid.js 的宇宙中心其实是图元。Solid 有着一套“完善的响应式图元”。这些图元功能类似 React 的 hook,但又有着些许不同。对 React 来说,hook 是独立于 React 的组件生命周期的,需要使用者对虚拟 DOM 和 React 的渲染生命周期有一定的了解。而它的这种独立性也就意味着开发者们需要下很深的工夫才能玩转 hook。首先让我们以 React 的组件为例:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `The current count is: ${count}`;
}, [count]);
return (
<div>
<p>The current count is: {count}</p>
<button onClick={() => setCount((x) => x + 1)}>Plus</button>
</div>
);
}
虽然这个例子并不难,但其中也涉及了几条必不可少的规则,忽略这些规则将带来一些不必要的麻烦。但 Solid 的响应式图元就不同了,它们可以让你用起来没有后顾之忧。你可以选择性地调用这些图元,就连组件里你都可以选择不调用。用 Solid 来重写上面的例子,就像这样:
const [count, setCount] = createSignal(0);
createEffect(() => {
document.title = `The current count is: ${count()}`;
});
function Counter() {
return (
<div>
<p>The current count is: {count()}</p>
<button onClick={() => setCount((x) => x + 1)}>Plus</button>
</div>
);
}
把所有的响应式图元全部挪到了组件之外、方法之前。我们能这么做的原因完全是因为 Solid 是以响应式图元本身为基础搭建的,而不是完全依赖组件。
响应式还是虚拟 DOM
就像是其他大众框架,“虚拟 DOM”这个概念是 React 框架的核心。虚拟 DOM 是指在内存中用于描述真实 DOM 的 JS 对象。因为创建虚拟 DOM 的成本要远小于创建一个真实的 DOM,所以多次运行组件或者对虚拟 DOM 进行更新时的消耗也会更小。除非是对真实的 DOM 进行修改,React 才会对虚拟 DOM 进行更新,使其与真实的 DOM 相符。想在 React 中熟练调用 hook,就意味着开发者们要对虚拟 DOM 的生命周期有一定的了解。
而另一方面,Solid.js 更依靠一套“完善的”响应式。举例来说,Solid 程序中的 JSX 语句依赖于一个响应式信号,也就是说,只有依赖于这个信号数值的 JSX 才会随着信号的变更在方法中被重新调用。同理,所有在 creatEffect 图元中运行的效果也是一样,如果这个效果依赖于一个或多个响应式数值,那么只有在这些数值变更时才会重新渲染。这样一来,依赖数组也不需要了,因为 Solid 已经提前知道那些数值可以变更,以及受这些响应式数值影响需要重新渲染的效果都有那些。
这也就是为什么前文会说 Solid 中组件“消失不见”,以及是如何在组件之外调用响应式图元的了。在初始的渲染之后,只有应用中“完善”的部分会继续存在。
来自 React 的“小玩笑”
搞清楚这两个框架的底层工作原理大概会让不少 React 开发者们在初次尝试 Solid 时手忙脚乱。首先你重新学习的就是,组件并不会重新运行。也就是说,我们在 React 中学到的大部分规矩都不再适用于 Solid.js。我们不需要用 useCallback 或者 useRef 来调用 hook,因为组件都是一次性的。而如果我们想要在 JavaScript 里赋值给变量,只需要这么做:
function MyForm() {
const handleSubmit = (e) => {
/* handl submit */
};
let myInput;
// use onMount or createEffect to read after connected to DOM
onMount(() => myInput.focus());
return (
<form onSubmit={handleSubmit}>
<input ref={myInput} />
</form>
);
}
第二点要重新熟悉的是,在销毁 prop 函数的时候,Solid.js 的那些细粒度的响应式也会被销毁,虽然你可能并没有在 prop 对象中存储任何响应式数值,但要记住,这些对象一旦被销毁,这些响应式也就全没了。这也就是并不推荐下面这种代码的原因:
function Greeting(props) {
const { greeting, name } = props;
return (
<h3>
{greeting} {name}
</h3>
);
}
但如果你写成这个样子,那么效果会好很多:
function Greeting(props) {
return (
<h3>
{props.greeting} {props.name}
</h3>
);
}
或者可以直接利用 Solid 的 mergeProps 或者 splitProps 函数,在不影响响应式的情况下对带有响应式数值的对象进行合并或拆散。
在 React 面世的时候,它挑战了当时最优秀的架构,并影响着我们现今编写网页应用的方式。而现在 Solid 要做的,就是再次重现当年 React 的影响。
原文链接:https://non-traditional.dev/an-intro-to-solidjs-for-react-developers