-
-
Notifications
You must be signed in to change notification settings - Fork 887
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
2,218 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# 与其他库进行比较 | ||
|
||
本节无法避免一些偏见,但我认为了解 Solid 的解决方案与其他库的差异很重要。这与性能无关。要对性能有明确的了解,请查看 [JS 框架基准测试](https://github.com/krausest/js-framework-benchmark)。 | ||
|
||
## React | ||
|
||
React 对 Solid 产生了很大的影响。React Hooks API 中的单向数据流和明确的读写分离影响了 Solid 的 API。Solid 更加像是一个 “渲染库” 而不是一个框架。Solid 对如何在应用程序开发中管理数据有着独到的看法,但并不试图约束执行。 | ||
|
||
然而,尽管 Solid 与 React 的设计理念保持一致,但它的底层运作方式完全不同。React 使用 Virtual DOM 而 Solid 没有。React 的抽象是自上而下的组件分区,其中渲染方法被重复调用和差异对比。相反,Solid 将每个模板整体渲染一次,构建其响应图,然后才执行与细粒度更改相关的指令。 | ||
|
||
#### 迁移建议: | ||
|
||
Solid 的更新模型完全不像 React,甚至不像 React + MobX。不要将函数组件视为 `render` 函数,而是将它们视为 `constructor`。注意解构或提前属性访问会丢失响应性。Solid 的 primitive 没有像 Hook 规则这样的限制,因此你可以根据需要自由嵌套它们。你不需要使用列表行上的显式 key 来实现具有 key 的行为。最后,没有 VDOM,所以像 `React.Children` 和 `React.cloneElement` 这样的命令式 VDOM API 毫无意义。我鼓励寻找不同的方法来解决以声明方式使用这些方法的问题。 | ||
|
||
## Vue | ||
|
||
Solid 在设计方面并没有特别受到 Vue 的影响,但它们在方法上来讲是可以进行比较的。他们都在响应式系统中使用 Proxy,并能基于读取的自动跟踪。但这就是所有的相似之处。Vue 的细粒度依赖检测只是提供给一个细粒度的虚拟 DOM 和组件系统,而 Solid 将其粒度保持在它的直接 DOM 更新上。 | ||
|
||
Vue 重视简单,而 Solid 重视透明度。尽管 Vue 与 Vue 3 的新方向更符合 Solid 所采用的方法。这些库可能会随着时间的推移更加一致,这取决于它们如何继续发展。 | ||
|
||
#### 迁移建议: | ||
|
||
作为另一个现代响应式库的 Vue 3, 迁移起来你应该会感觉很熟悉。Solid 的组件非常类似于在 Vue 的 `setup` 函数末尾标记模板。小心地同函数来计算覆盖状态推导。响应性无处不在。Solid 的代理有意设置为只读。在你尝试之前不要批评它。 | ||
|
||
## Svelte | ||
|
||
Svelte 开创了 Solid 在一定程度上也采用的预编译消失型框架。这两个库都是真正的响应式,可以生成非常小的执行代码包,尽管 Svelte 是小型 demo 的赢家。Solid 需要在其声明中更加明确,更少依赖编译器的隐式分析,但这正是 Solid 卓越性能的一部分。Solid 还在运行时保留了更多东西,这在更大的应用程序中可以更好地扩展。Solid 的 RealWorld 演示实现比 Svelte 的小 25%。 | ||
|
||
这两个库都旨在帮助他们的开发人员编写更少的代码,但方法却完全不同。Svelte 3 专注于优化处理本地化更改的易用性,重点是普通对象交互和双向绑定。相比之下,Solid 通过故意采用 CQRS 和不可变接口来专注于数据流。通过功能模板组合,在许多情况下,Solid 允许开发人员编写比 Svelte 更少的代码,尽管 Svelte 的模板语法绝对更简洁。 | ||
|
||
#### 迁移建议: | ||
|
||
开发体验非常不同,虽然有些东西是相似的,但这是一种非常不同的体验。Solid 中的组件消耗低,所以不要担心拥有更多的组件。 | ||
|
||
## Knockout.js | ||
|
||
这个库的存在归功于 Knockout。该项目的动机是将其用于细粒度依赖性检测的模型现代化。Knockout 于 2010 年发布,支持 Microsoft Explorer 回到 IE6,而 Solid 的大部分内容根本不支持 IE。 | ||
|
||
Knockout 的绑定只是在运行时遍历的 HTML 中的字符串。它们取决于克隆上下文($parent 等...)。而 Solid 使用 JSX 或 JavaScript API 的标签模板字面量来模板化。 | ||
|
||
最大的区别可能是 Solid 的批处理更改方法可确保同步性,而 Knockout 具有使用延迟微任务队列的 deferUpdates。 | ||
|
||
#### 迁移建议: | ||
|
||
如果你习惯了 Knockout,Solid 的 primitive 对你来说可能看起来很奇怪。读/写分离是有意的,而不仅仅是为了让生活更艰难。可以采用状态/动作 (Flux) 心智模型。虽然这些库看起来相似,但它们提倡不同的最佳实践。 | ||
|
||
## Lit & LighterHTML | ||
|
||
这些库非常相似,并且对 Solid 产生了一些影响。大多数情况下,Solid 的编译代码使用非常相似的方法来高效地初始渲染 DOM。克隆模板元素和使用注释占位符是 Solid 和这些库的共同点。 | ||
|
||
最大的区别是,虽然这些库不使用虚拟 DOM,但它们以相同的方式处理渲染,自上而下,需要组件分区以保持正常。相比之下,Solid 使用其细粒度的响应式图仅更新已更改的内容,并且这样做仅在其初始渲染中技术与之雷同。这种方法利用了仅适用于原生 DOM 的初始速度,并且还具有最高性能的更新方法。 | ||
|
||
#### 迁移建议: | ||
|
||
这些库非常小,而且很容易在上面构建。但是,请记住,`<MyComp/>` 不仅仅是 HTMLElement(数组或函数)。尝试将你的东西保存在 JSX 模板中。提升在大多数情况下都有效,但最好在心理上将其视为渲染库而不是 HTMLElement 工厂。 | ||
|
||
## S.js | ||
|
||
这个库对 Solid 的反应式设计影响最大。Solid 在内部使用了 S.js 几年,直到特征需求使它们走上了不同的道路。S.js 是迄今为止最高效的响应式库之一。它像数字电路一样对所有同步时间步长进行建模,并确保一致性,而无需执行诸如 MobX 之类的库中的许多更复杂的机制。Solid 的反应性归根结底是 S 和 MobX 之间的一种混合。这使它比大多数反应式库(Knockout、MobX、Vue)具有更高的性能,同时为开发人员保留了易于使用的心智模型。S.js 最终仍然是性能更高的反应式库,尽管除了最严厉的综合基准测试之外,差异几乎不明显。 | ||
|
||
## RxJS | ||
|
||
RxJS 是一个响应式库。虽然 Solid 对 Observable 数据有类似的想法,但它有着许多观察者模式不同的应用。虽然 Signal 就像一个 Observable 的简单版本(只有 next),但自动依赖检测的模式取代了 RxJS 的一百个左右的操作符。Solid 有能力采用这种方法,事实上,该库的早期版本也包含类似的运算符,但在大多数情况下,在计算中编写自己的转换逻辑更为直接。在 Observable 是冷启动、单播和基于推送的情况下,客户端上的许多情况都是热启动和多播,这也是 Solid 的默认行为。 | ||
|
||
## 其他 | ||
|
||
Angular 和其他一些流行的库在这个比较中明显缺失。缺乏对这些库的使用经验会妨碍进行任何充分的比较。一般来说,Solid 与较大的框架几乎没有共同之处,而且很难直接比较它们。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# 常见问题 | ||
|
||
### 1. 没有 VDOM 的 JSX? 这是 [雾件](https://zh.wikipedia.org/wiki/%E9%9C%A7%E4%BB%B6)吗? 我听过像其他框架的作者强调说这是不可能的。 | ||
|
||
当你没有 React 的更新模型时,这是可能的。JSX 是一个模板 DSL,就像任何其他的模板一样。只是在某些方面更灵活的一种。插入任意 JavaScript 有时可能具有挑战性,但与支持扩展运算符没有什么不同。所以不,这不是雾件,而是一种被证明是最高效的方法。 | ||
|
||
真正的好处在于它的可扩展性。你有编译器为你工作,为你提供最佳的原生 DOM 更新,但你拥有像 React 这样的库的所有自由,可以使用诸如 Render Props 和高阶组件之类的技术以及响应式钩子来编写组件。不喜欢 Solid 的流程控制? 写你自己的。 | ||
|
||
### 2. Solid 的性能如何? | ||
|
||
我希望我只需要指出一件事,但它确实是最重要的许多选择决定的: | ||
|
||
1. 显式响应性,因此只跟踪应该反应性的事物。 | ||
2. 编译时考虑到初始创建。Solid 使用启发式算法来松散粒度以减少计算次数,同时保持关键更新的粒度和性能。 | ||
3. 响应式表达式只是函数。这使得 "消失的组件" 能够通过惰性 props 求值移除不必要的包装器和同步开销。 | ||
|
||
这些是目前独特的技术组合,使 Solid 在竞争中具有优势。 | ||
|
||
### 3. 有 React 兼容包吗? | ||
|
||
不会。而且可能永远不会有。虽然 API 是相似的,并且组件通常可以通过小的编辑距离策略来移动,但更新模型根本不同。React 组件会一遍又一遍地渲染,因此 Hooks 之外的代码的运行方式非常不同。闭包和钩子规则不仅是不必要的,它们还可以以在 Solid 不起作用的方式使用。 | ||
|
||
另一方面,Vue-compat 是可行的。虽然目前没有实施的计划。 | ||
|
||
### 4. 为什么解构不起作用?我觉得到我可以通过将整个组件包装在一个函数中来修复它。 | ||
|
||
响应性发生在 Prop 和 Store 对象的属性访问上。在绑定或响应式计算之外引用它们将不会被跟踪。在这些情况下,解构正常工作。 | ||
|
||
但是,将整个组件包装在一个函数中并不是你想不负责任地做的事情。Solid 没有 VDOM。因此,任何跟踪的更改都会再次运行整个函数,重新创建所有内容。不要这样做。 | ||
|
||
### 5. 你能添加对 Class 组件的支持吗? 我发现生命周期更容易推理。 | ||
|
||
不打算支持类组件。Solid 的生命周期与调度反应系统相关,并且是人为的。我想你可以用它创建一个类,但实际上所有非事件处理器代码基本上都在构造函数中运行,包括渲染函数。这只是你不细化数据要求更多语法的借口。 | ||
|
||
将数据及其行为组合在一起,而不是将生命周期组合在一起。这是一种已经奏效了数十年的响应式最佳实践。 | ||
|
||
### 6. 我真的不喜欢 JSX,模板 DSL 有机会吗? 哦,我看到你标记了标签模板字面量/HyperScript。也许我会用那些... | ||
|
||
别。我会马上阻止你。我们像 Svelte 使用他们的模板一样使用 JSX 来创建优化的 DOM 指令。标签模板字面量 和 HyperScript 解决方案确实令人印象深刻,但除非你有真正的理由,例如无构建要求,否则它们在各方面都较差。较大的包、较慢的性能以及需要手动解决方法包装值。 | ||
|
||
有选择是件好事,但 Solid 的 JSX 确实是最好的解决方案。模板 DSL 也很棒,虽然限制更多,但 JSX 为我们免费提供了很多。现有解析器、语法高亮、TypeScript、Prettier、代码完成,以及最后但并非最不重要的 TypeScript。 | ||
|
||
我知道其他库一直在增加对这些功能的支持,但这是一项巨大的努力,但仍然不完美,并且一直是维护问题。这也确实是一种务实的态度。 | ||
|
||
### 7. 我什么时候使用 Signal 或者 Store? 它们有何不同? | ||
|
||
存储自动包装嵌套值,使其成为深层数据结构,是数据模型的理想选择。对于大多数其他情况,Signal 是轻量级的,并且可以出色地完成工作。 | ||
|
||
尽管我很想将这些包装在一起作为一个单一的 API,但你不能代理基本数据类型。函数是最简单的接口,任何响应式式表达式(包括状态访问)都可以在传输时包装成一个,如果这提供了一个通用 API。你可以随意命名你的 signal 和状态,并且它保持最小。我想要做的最后一件事是在用户端强制键入 `.get()` `.set()` 或更糟的 `.value`。为了简洁起见,至少前者可以使用别名,而后者只是调用函数的最简单的方法。 | ||
|
||
### 8.为什么我不能像在 Vue.js Svelte MobX 中那样为 Solid 的 Store 赋值? 有双向绑定么? | ||
|
||
响应性是一种强大的工具,但也是一种危险的工具。MobX 知道这一点并引入了严格模式和 Action 来限制更新发生的位置/时间。在 Solid 处理整个组件数据树时,我意识到我们可以从 React 中学到一些东西。只要你提供拥有相同约定的方法,你就不需要实际上是不可变的数据。 | ||
|
||
能够传递更新状态的能力可以说比决定传递状态更重要。因此,能够将其分开很重要,而且只有在读取不可变的情况下才有可能。如果我们仍然可以粒度更新,我们也不需要付出不可变的成本。幸运的是,ImmutableJS 和 Immer 有大量的现有技术。具有讽刺意味的是,Solid 主要用作具有可变内部结构和不可变接口,和 Immer 刚好相反。 | ||
|
||
### 9. 我可以单独使用 Solid 的响应性吗? | ||
|
||
|
||
当然。虽然我没有导出一个独立的包,但很容易在没有编译器的情况下安装 Solid,只需使用反应 primitive。粒度响应性的好处之一是它与库无关。就此而言,几乎每个反应式库都是这样工作的。这启发了 [Solid](https://github.com/solidjs/solid) ,Solid 内部使用 [DOM 表达式库](https://github.com/ryansolid/dom-expressions) 并根据纯粹的响应式系统来创建的渲染器。 | ||
|
||
列出一些可尝试的库: [Solid](https://github.com/solidjs/solid), [MobX](https://github.com/mobxjs/mobx), [Knockout](https://github.com/knockout/knockout), [Svelte](https://github.com/sveltejs/svelte), [S.js](https://github.com/adamhaile/S), [CellX](https://github.com/Riim/cellx), [Derivable](https://github.com/ds300/derivablejs), [Sinuous](https://github.com/luwes/sinuous), 甚至最近的 [Vue](https://github.com/vuejs/vue). 制作响应式库比将其标记到渲染器上工作量要多得多,例如 [lit-html](https://github.com/Polymer/lit-html),但这有助于帮你找到感觉。 | ||
|
||
### 10. Solid 有我可以使用的 Next.js 或 Material Components 之类的库吗? | ||
|
||
据我所知没有。如果你有兴趣构建一个,我可以随时在我们的 [Discord](https://discord.com/invite/solidjs) 上帮助你构建它们。我们有基础,只需要在这些基础上再接再厉。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
# 让我们开始吧 | ||
|
||
## 使用 Solid | ||
|
||
到目前为止,开始使用 Solid 的最简单方法是在线使用。在我们的 REPL(https://playground.solidjs.com) 上尝试各种想法是一种不错方式。另外你也可以在 https://codesandbox.io/ 修改我们的示例代码。 | ||
|
||
你也可以通过在终端中运行以下命令来创建并启动简单的 Vite 模板项目: | ||
|
||
```sh | ||
> npx degit solidjs/templates/js my-app | ||
> cd my-app | ||
> npm i # or yarn or pnpm | ||
> npm run dev # or yarn or pnpm | ||
``` | ||
|
||
使用 TypeScript : | ||
|
||
```sh | ||
> npx degit solidjs/templates/ts my-app | ||
> cd my-app | ||
> npm i # or yarn or pnpm | ||
> npm run dev # or yarn or pnpm | ||
``` | ||
|
||
## 学习 Solid | ||
|
||
Solid 到处都是可组合的小片段,用这些片段用来构建应用块。这些部分主要由许多浅显的顶级 API 的函数组成。幸运的是,你无需了解其中的大部分内容即可开始使用。 | ||
|
||
你可以使用组件和响应式 Primitives 这两种主流方式来构建区块 | ||
|
||
组件是接受 props 对象并返回 JSX 元素(包括原生 DOM 元素和其他组件)的函数。它们可以用大驼峰拼写表示为 JSX 元素 | ||
|
||
```jsx | ||
function MyComponent(props) { | ||
return <div>Hello {props.name}</div>; | ||
} | ||
|
||
<MyComponent name="Solid" />; | ||
``` | ||
|
||
组件是轻量级的,因为它们本身没有状态,也没有实例。相反,它们充当 DOM 元素和响应式 primitives 的工厂函数 | ||
|
||
Solid 的细粒度响应式建立在 3 个简单的 primitives 之上:Signals、Memos 和 Effects。它们共同构成了一个自动跟踪同步引擎,可确保你的视图保持最新。响应式计算采用了简单包装函数表达式的形式,另外他们是同步执行的 | ||
|
||
```js | ||
const [first, setFirst] = createSignal("JSON"); | ||
const [last, setLast] = createSignal("Bourne"); | ||
|
||
createEffect(() => console.log(`${first()} ${last()}`)); | ||
``` | ||
|
||
你可以在以下内容中了解更多 [Solid's Reactivity](https://www.solidjs.com/docs/latest#reactivity) 和 [Solid's Rendering](https://www.solidjs.com/docs/latest#rendering). | ||
|
||
## Solid 理念 | ||
|
||
Solid 的设计提出了一些可以帮助我们最好地构建网站和应用程序的原则和价值观。当你了解 Solid 背后的哲学时,学习和使用 Solid 会更容易。 | ||
|
||
### 1. 声明式数据 | ||
|
||
声明式数据是将数据行为的描述与其声明联系起来的实践。这允许我们通过将数据行为的所有方面打包在一个地方来轻松组合。 | ||
|
||
### 2. 消失的组件 | ||
|
||
在不考虑更新的情况下构建组件已经够难的了。Solid 的组件更新是彼此完全独立的。组件函数被调用一次,然后就不再存在。组件的存在是为了组织你的代码,而不是其他。 | ||
|
||
### 3. 读/写 分离 | ||
|
||
精确的控制和可预测性有助于打造更好的系统。我们不需要真正的不变性来强制执行单向数据流,只需要能够有意识到哪些消费者可能会写,哪些可能不会。 | ||
|
||
### 4. 简单胜于容易 | ||
|
||
细粒度响应性教会我们:明确且一致的约定即使需要更多努力也是值得的。且有必要提供最少的工具作为构建的基础。 | ||
|
||
## Web 组件 | ||
|
||
Solid 生而将 Web 组件作为一等公民。随着时间的推移,它的设计不断发展,目标也发生了变化。然而,Solid 仍然是编写 Web 组件的好选择。[Solid Element](https://github.com/solidjs/solid/tree/main/packages/solid-element) 允许你编写和包装 Solid 的函数组件以生成小型且高性能的 Web 组件。在 Solid 应用程序中,Solid Element 仍然能够利用 Solid 的 Context API,并且 Solid 的 Portals 支持隔离样式的 Shadow DOM 。 | ||
|
||
## 服务端渲染 | ||
|
||
Solid 拥有动态的服务器端渲染解决方案,可实现真正的同构开发体验。通过使用我们的 Resource primitive,很容易进行异步数据请求,更重要的是,我们也可以在客户端和浏览器之间自动序列化和同步。 | ||
|
||
由于 Solid 支持服务器上的异步和流式渲染,因此你可以以一种方式编写代码并让它在服务器上执行。这个特性类似 [render-as-you-fetch](https://reactjs.org/docs/concurrent-mode-suspense.html#approach-3-render-as-you-fetch-using-suspense),并且代码分割特性也适用于 Solid。 | ||
|
||
更多信息,请阅读 [服务端渲染指南](https://www.solidjs.com/docs/latest#server-side-rendering). | ||
|
||
## 无编译? | ||
|
||
不喜欢 JSX?不介意手动包装表达式、性能更差和包大小更大吗?你可以采用另一种方案:在非编译环境中使用标记模板字面量或 HyperScript 创建 Solid 应用。 | ||
|
||
你可以直接在浏览器中运行下面代码 [Skypack](https://www.skypack.dev/): | ||
|
||
```html | ||
<html> | ||
<body> | ||
<script type="module"> | ||
import { createSignal, onCleanup } from "https://cdn.skypack.dev/solid-js"; | ||
import { render } from "https://cdn.skypack.dev/solid-js/web"; | ||
import html from "https://cdn.skypack.dev/solid-js/html"; | ||
const App = () => { | ||
const [count, setCount] = createSignal(0), | ||
timer = setInterval(() => setCount(count() + 1), 1000); | ||
onCleanup(() => clearInterval(timer)); | ||
return html`<div>${count}</div>`; | ||
}; | ||
render(App, document.body); | ||
</script> | ||
</body> | ||
</html> | ||
``` | ||
|
||
请记住,你仍然需要相应的 DOM 表达式库才能配合 TypeScript 使用。你也可以搭配 [Lit DOM Expressions](https://github.com/ryansolid/dom-expressions/tree/main/packages/lit-dom-expressions) 使用标签模板字面量或者搭配 [Hyper DOM Expressions](https://github.com/ryansolid/dom-expressions/tree/main/packages/hyper-dom-expressions) 使用 HyperScript。 |
Oops, something went wrong.