简介
SolidJS 是不断增长的 JavaScript 框架列表中的一个新成员。但它并不是您普通的、日常的框架。SolidJS有一些相当有趣的功能,给竞争者带来了激烈的热度。
SolidJS框架由Ryan Carniato创建,于2018年开源,但最近以其 "细粒度反应性 "的卖点获得了一些人气。
SolidJS与React的理念几乎完全相同,但有几个例外。例如,没有虚拟DOM,以及只渲染一次组件。这些特性有助于用SolidJS构建的应用程序拥有极快的速度,并使其成为当今最快的JavaScript框架之一。
在本教程中,我们将探索 SolidJS 如何工作。为此,我们将建立一个 "待办事项 "示例应用程序,以展示这个伟大的框架所能提供的功能。
开始学习
有两种方法可以开始使用 SolidJS。首先是使用他们的在线 REPL,当您想快速建立原型时,这很有用。第二种是通过克隆 SolidJS 团队制作的预先存在的模板。
我们将采用后一种方法,因为它对本指南的目的更方便。
有两个可用的模板,即vanilla JavaScript版本或TypeScript版本。在这个介绍中,我们将使用vanilla JavaScript版本。
要开始使用该模板,请在终端运行以下命令:
# Create a solid app from the template
npx degit solidjs/templates/js solid-todo
# Change directory to project folder
cd solid-todo
# Install dependencies
npm i # or yarn install
# Start local server
npm run dev # or yarn dev
在最后一条运行本地开发服务器的命令执行完毕后,在浏览器上进入http://localhost:3000/,查看正在运行的应用程序。

Solid 组件
所有的JavaScript框架都是建立在组件的概念上。组件是一个应用程序的小隔断,像一个表单、一个输入字段或一个页脚。
这里有一个示例Solid 组件。
#App.jsx
import styles from "./App.module.css";
function App() {
return (
<div class={styles.App}>
<header class={styles.header}>
<img src={logo} class={styles.logo} alt="logo" />
<p>
Edit src/App.jsx and save to reload. </p> <a class={styles.link} href="https://github.com/solidjs/solid" target="_blank" rel="noopener noreferrer" > Learn Solid </a> </header> </div> ); } export default App;
就像React一样,Solid 组件是用JSX编写的。正如您在上面的代码块中所看到的,SolidJS 组件基本上是一个巨大的 JavaScript 函数,它返回 HTML 和 JavaScript 代码的混合,被称为 JSX。
信号
信号是 SolidJS 中反应性的基础。它们包含值,每当该值发生变化时,它们就会在被使用的每个实例中自动更新。
要创建一个信号,首先我们需要从solid-js 导入createSignal 并在我们的组件中使用它:
import {createSignal} from "solid-js"
const [count, setCount] = createSignal(0);
createSignal 接受两个值,一个getter和一个setter。第一个值是一个返回当前值的函数,而不是值本身。
这意味着每当我们需要访问当前值时,我们要这样做:
return <div>Count: {count()}</div>;
商店
商店是 SolidJS 处理嵌套反应性的方式。商店的返回值是一个代理对象,其属性可被跟踪。
我们可以像这样创建和使用一个商店。
# First import createStore at the top of your component
import { createStore } from "solid-js/store"
#
const [todos, setTodos] = createStore({
items: [
{ text: "Go skydiving", id: 3 },
{ text: "Go surfing", id: 2, },
{ text: "Climb Everest", id: 1 }
],
counter: 4,
})
const addTodoItem = (input) => {
const title = input.value;
if (!title.trim()) return;
setTodos({
items: [{ text: title, id: todos.counter }, ...todos.items],
counter: todos.counter + 1
});
input.value = "";
}
return (
<div>
<input type="text" ref={input}
placeholder="What do you have to do today?" name="todo"
onKeyDown={(e) => {
if (e.key === "Enter") {
addTodoItem(input);
}
}}>
</input>
<ul>
{todos.items.map(i => (
<li>{i.text}</li>
))}
</ul>
</div>
);
上面的代码是完整演示的一个小样本。一个输入字段将被呈现在屏幕上,当用户通过输入任务并点击 "回车 "进行交互时,"待办 "项目的列表将被更新并呈现在一个列表中。
从存储器中访问任何值只能通过存储器中的一个属性来完成,而不是通过使用顶层状态,这就是为什么我们在第17行使用todos.items ,而不是todos 来传播items 数组。
生命周期方法
生命周期方法是内置于 SolidJS 的特殊方法,用于在组件在 DOM 中的持续时间内对其进行操作。SolidJS 有几个生命周期,如onMount 和onCleanup 。
onMount 生命周期是在我们需要在组件最初渲染时运行一段代码时使用的。
# First import onMount at the top of your component
import { onMount } from "solid-js"
import { createStore } from "solid-js/store"
const [todos, setTodos] = createStore({
items: [],
counter: 3,
})
onMount(() => {
setTodos("items", [
{ text: "Go swimming", id: 2 },
{ text: "Go scuba diving", id: 1 }
])
})
从上面的代码块中,注意到商店已经被修改,它的内容被移到了onMount 生命周期的钩子上。当组件第一次渲染时,items 数组被填满了我们的待办事项列表。
onCleanup 生命周期方法被用来在有副作用的函数之后进行任何必要的清理。
import { createSignal, onCleanup } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
const timer = setInterval(() => setCount(count() + 1), 1000);
onCleanup(() => clearInterval(timer));
return <div>Count: {count()}</div>;
}
控制流
Solid JS 有一堆内置的帮助器,用于在需要执行各种操作时,如
条件性渲染或在一个数组列表中循环。这些帮助器可避免在每次更新时浪费地重新创建所有的 DOM 节点。
这里有一个代码块,演示了它们的使用方法。
import { Show, Switch, Match, Portal, For } from "solid-js";
<Show
when={loggedIn()}
fallback={() => <button onClick={toggle}>Log in</button>}
>
<button onClick={toggle}>Log out</button>
</Show>
<For each={todos.items}>{(todo) =>
<li>
<div class={styles.todoItem}>
{todo.text}
<i class="fa fa-minus-circle" onClick={() => {
removeTodoItem(todo.id);
}}>
</i>
</div>
</li>
}
</For>
<Portal>
<div class="popup">
<h1>Popup</h1>
<p>Some text you might need for something or other.</p>
</div>
</Portal>
<Switch fallback={<p>{x()} is between 5 and 10</p>}>
<Match when={x() > 10}>
<p>{x()} is greater than 10</p>
</Match>
<Match when={5 > x()}>
<p>{x()} is less than 5</p>
</Match>
</Switch>
让我们看一下上面的代码块中发生了什么。
Show 有条件地显示或隐藏元素,For 循环浏览一个项目列表,Portal 在应用程序的正常流程之外插入元素,Switch 根据某些条件渲染元素。
创建我们的视图
我们将开始为我们的待办事项应用程序创建各种视图。总的来说,我们将创建两个新的组件:一个Todolist.jsx 和About.jsx 组件,以及一个用于Todolist.jsx 组件的样式表Todolist.module.css 。
要做到这一点,首先在项目的根目录src 创建一个components 文件夹,并创建上述组件。
依次运行下面的命令来实现上述说明。
# navigate to the src folder
cd src
#create the components folder
mkdir components
#navigate to the components folder
cd components
#create the Todolist and About component and stylesheet
touch Todolist.jsx Todolist.module.css About.jsx
待办事项列表组件
Todolist.jsx 组件将包含输入字段和用户记录的所有待办事项的列表。
用以下代码更新Todolist.jsx 组件。
//Todolist.jsx
import styles from "./Todolist.module.css"
import { For, onMount } from "solid-js"
import { createStore } from "solid-js/store"
function TodoList() {
let input;
const addTodoItem = (input) => {
const title = input.value;
if (!title.trim()) return;
setTodos({
items: [{ text: title, id: todos.counter }, ...todos.items],
counter: todos.counter + 1
});
input.value = "";
}
const removeTodoItem = (index) => {
setTodos('items', (t) => t.filter((item) => item.id !== index))
}
onMount(() => {
setTodos("items", [
{ text: "Go swimming", id: 2 },
{ text: "Go scuba diving", id: 1 }
])
})
const [todos, setTodos] = createStore({
items: [],
counter: 3,
})
return (
<>
<div class={styles.container}>
<input type="text" ref={input}
placeholder="What do you have to do today?" name="todo"
onKeyDown={(e) => {
if (e.key === "Enter") {
addTodoItem(input);
}
}}>
</input>
<ul class={styles.todoList}>
<For each={todos.items}>{(todo) =>
<li>
<div class={styles.todoItem}>
{todo.text}
<i class="fa fa-minus-circle" onClick={() => {
removeTodoItem(todo.id);
}}></i>
</div>
</li>
}
</For>
</ul>
</div>
</>
);
}
export default TodoList
下面,让我们为Todolist.jsx 组件添加CSS样式。
// Todolist.module.css
.container {
background: #fff;
}
.todoList {
margin: 0;
padding: 0;
list-style-type: none;
}
.todoList li {
padding: 20px;
font-size: 1.3em;
background-color: #E0EDF4;
border-left: 5px solid #3EB3F6;
margin-bottom: 2px;
color: #3E5252;
}
input {
width: calc(100% - 40px);
border: 0;
padding: 20px;
font-size: 1.3em;
background-color: #323333;
color: #687F7F;
}
li .todoItem{
display:flex;
justify-content: space-between;
}
.todoItem i{
cursor: pointer;
}
About 组件
要创建about 组件,将以下代码添加到About.jsx 。
function About() {
return (
<div>
<h1>About Page</h1>
<div>
<p>This is an about page created to demonstrate routing in Solid JS. Lorem ipsum dolor sit amet consecteturadipisicing elit. Tenetur, omnis?
</p>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Maiores deserunt neque ad nihil! Ut fugit mollitia voluptatum eaque. Impedit repudiandae aut eveniet eum. Nisi, quisquam enim ut, illo ipsum unde error a voluptates nobis, corporis mollitia aliquam magnam. Ipsam veniam molestias soluta quae fugiat ipsum maiores laboriosam impedit minus quisquam!
</p>
</div>
</div>
);
}
export default About;
路由
就像其他框架一样,SolidJS 有它自己处理路由的方式,那就是使用户在网站上的各个页面之间移动。
要在 SolidJS 中实现路由,我们首先要安装它。
yarn add solid-app-router
#OR
npm i solid-app-router
接下来,我们将配置路由,并创建用户可以用来在页面之间移动的链接。
要做到这一点,让我们移动到我们的App.jsx 文件,删除所有的标记,并用下面的代码来代替它。
//App.jsx
import styles from "./App.module.css";
import { Router, Routes, Route, Link } from "solid-app-router";
import { lazy } from "solid-js";
const Todolist = lazy(() => import("./components/Todolist"));
const About = lazy(() => import("./components/About"));
function App() {
return (
<>
<Router>
<div class={styles.app}>
<Link href="/">Link to Home Page</Link>
<Link href="/about">Link to About Page</Link>
<Routes>
<Route path="/" element={<Todolist />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
</Router>
</>
);
}
export default App;
在导入我们的全局样式表后,我们从 solid-app-router 中导入Router,Routes,Route, 和Link ,以使我们的路由器配置工作得以进行。接下来,我们从 SolidJS 中导入lazy ,以帮助我们懒散地加载我们的路由。
在利用懒惰加载功能的同时导入路由的代码如下。
const Todolist = lazy(() => import("./components/Todolist"));
接下来,我们要在Router 标签之间包裹我们的应用程序,并将我们的路由定义为这样。
<Routes>
<Route path="/" element={<Todolist />} />
<Route path="/about" element={<About />} />
</Routes>
然后,我们需要添加导航链接,以便用户能够在路由之间切换。
<Link href="/">Link to Home Page</Link>
<Link href="/about">Link to About Page</Link>
让我们更新全局样式表上的样式,App.module.css 。
body, html {
margin: 0;
height: 100%;
}
.app {
width: 100%;
}
body {
background-color: #EEEEEE;
font-family: 'Montserrat', sans-serif;
padding: 50px 50px 0 50px;
}
nav {
padding: 20px 20px 20px 0;
}
nav a {
padding: 10px;
text-decoration: none;
background: #fff;
border-radius: 3px;
color: rgb(0, 110, 255);
font-weight: bold;
margin-right: 15px;
}
这就是我们的应用程序现在的样子。


结语
我们已经经历了 SolidJS 的一些基本功能,并成功地建立了一个小型的待办事项列表应用程序,演示了其中的一些功能。还有许多有趣的功能无法在此介绍中讨论,所以请随时查看Solid JS 文档网站以获得更多信息。