背景
今天被战友种草了一款前端框架,打开链接看文章,在各个指标的比较下,SolidJs
脱颖而出,下面简单介绍一下这个框架,然后开始记录一下学习笔记。(Golang的事情暂时放一放,毕竟咱是专业前端「手动狗头」)。
SolidJs简介
SolidJs
作为一个新星)可谓是各个厂牌的集大成者,它支持JSX
、Fragments
、Context
、Portals
、Lazy
等等,而且是继HyperApp和Svelte后,第三个比纯JS实现更小的库。
JSX + template
SolidJs
在保持JSX语法的同时,做了一些template的规范,比如它的For
、Index
、Switch
、Match
.....既保留了JSX语法的灵活性
,又再某些程度提高了编译速度
,perfect!
NO DOM DIFF
SolidJs
并不像Vue
&React
采用了虚拟dom,解决了内存占用过多的问题
SolidJs教程
Hello World
import { render } from 'solid-js/web' function HelloWorld() { return <p>Hello World</p> } render(() => <HelloWorld />, document.getElementById("app")) 复制代码
组件的使用
在SolidJs
中,组件的含义与React
基本一致,即组件即函数,举个🌰
nested.tsx
export default () => <p>from nested.tsx</p> 复制代码
main.tsx
import {render} from 'solid-js/web' import Nested from './nested' function App() { return ( <Nested /> ) } render(() => <App />, document.getElementById("app")) 复制代码
Fragment
如果返回了2个元素,可以用Fragement包装起来,举个🌰
import {render} from 'solid-js/web' import Nested from './nested' function App() { return ( <> <h1>组件:Nested</h1> <Nested /> </> ) } render(() => <App/>, document.getElementById('app')) 复制代码
Signal
Signal
是最核心的响应式基本要素,创建一个Signal
的语法为:
const [count, setCount] = createSignal(0) 复制代码
当然,createSignal
需要从solid-js
中导出:
import { createSignal } from 'solid-js' 复制代码
传递给createSignal
的值就是count
的初始值,返回一个带有2个函数的数组,一个是getter
,一个是setter
,可以使用解构随意命名这些参数,例如上面那个🌰,count就是getter,setCount就是setter。
注意:第一个返回值是一个getter
,而不是0
值本身。
举一个计数器的🌰:
import { render } from 'solid-js/web' import { createSignal } from 'solid-js' function App() { const [count, setCount] = createSignal(0) setInterval(() => { setCount(count() + 1) }, 1000) return ( <div>count的值为:{count()}</div> ) } render(() => <App/>, document.getElementById('app')) 复制代码
setCount也可以接受一个函数,举个🌰
setInterval(() => { setCount(c => c +1 ) }, 1000) 复制代码
派生Signal
类似于Vue
中的computed,举个🌰
import { render } from 'solid-js/web' import { createSignal } from 'solid-js' function App() { const [count, setCount] = createSignal(0) const doubleCount = () => count() * 2 return ( <> <div>doubleCount: {doubleCount()}</div> <button onClick={() => setCount(c => c+1)}>点击</button> </> ) } render(() => <App/>, docu3ment.getElementById('app')) 复制代码
Effect
通过从sloid-js导入createEffect
来创建Effect,接受一个函数,并监视其执行情况。createEffec 会自动订阅在执行期间读取的所有Signal,并在这些Signal值其中一个发生变化时,重新运行该函数,举个🌰
import { render } from 'solid-js/web' import { createSignal, createEffect } from 'solid-js' function App() { const [count, setCount] = createSignal(0) createEffect(() => { console.log("当前 count:", count()) }) return ( <button onClick={() => setCount(c => c + 1)}>点击</button> ) } render(() => <App/>, document.getElementById('app')) 复制代码
Show
Show
组件用来控制DOM是否显示,举个🌰
import { render } from 'solid-js/web'; import { createSignal, Show } from 'solid-js'; function App() { const [loggedIn, setLoggedIn] = createSignal(false); const toggle = () => setLoggedIn(!loggedIn()) return ( <Show when={loggedIn()} fallback={() => <button onClick={toggle}>Log in</button>}> <button onClick={toggle}>Log out</button> </Show> ) } render(() => <App />, document.getElementById('app')) 复制代码
与Vue
不同的是,solidJs是在Show组件中通过when
关键字后加条件来判断是否显示,通过fallback
关键字来显示上一个条件为false
时的组件。
For
For
是SolidJs遍历的列表组件,举个🌰
import { render } from 'solid-js/web'; import { createSignal, For } from 'solid-js'; function App() { const [dogs, setDogs] = createSignal([ { id: 0, name: "dog1" }, { id: 1, name: "dog2" }, { id: 2, name: "dog3" }, ]); return ( <For each={dogs()}> {(dog, i) => ( <div> id:{i()}; name:{dog.name} </div> )} </For> ); } render(() => <App />, document.getElementById('app')) 复制代码
Index
Index
组件也是用来遍历的,上面的🌰,用Index
组件进行遍历:
<Index each={dogs()}> {(dog, i) => ( {i}, {dog().name} )} </Index> 复制代码
可以发现,Index
与For
具有相似的功能,不同点是,For
中索引是Signal
,Index
中数据项是Signal
Switch
Switch
组件是Show
组件的扩展,如果在一个多层条件判断的情况下,使用Show
组件会发生多级嵌套,代码臃肿,使用Switch
/Match
可以很好的解决这种情况,举个🌰
import { render } from 'solid-js/web'; import { createSignal, Switch, Match } from 'solid-js' function App() { const [age, setAge] = createSignal(12); return ( <Switch fallback={<div>未知</div>}> <Match when={age() < 18}> <div>未成年</div> </Match> <Match when={age() >= 18 && age() < 60}> <div>成年人</div> </Match> <Match when={age() >= 60}> <div>老年人</div> </Match> </Switch> ); } render(() => <App />, document.getElementById('app')) 复制代码
Dynamic
Dynamic
组件处可以让开发者编写<Switch>/<Match>
组件变得更简练,举个栗子:
import { render, Dynamic } from "solid-js/web"; import { createSignal, Switch, Match, For } from "solid-js"; const RedThing = () => <strong style="color: red">Red Thing</strong>; const GreenThing = () => <strong style="color: green">Green Thing</strong>; const BlueThing = () => <strong style="color: blue">Blue Thing</strong>; const options = { red: RedThing, green: GreenThing, blue: BlueThing, }; function App() { const [selected, setSelected] = createSignal("red"); return ( <> <select value={selected()} onInput={(e) => setSelected(e.currentTarget.value)} > <For each={Object.keys(options)}> {(color) => <option value={color}>{color}</option>} </For> </select> <Dynamic component={options[selected()]} /> </> ); } render(() => <App />, document.getElementById("app")); 复制代码
Portal
Portal
组件可以在正常顺序之外插入元素,默认情况下,Portal
的子内容将从正常的DOM顺序中拿出来,插入到document.body下的
中,举个🌰
main.tsx
import { render, Portal } from "solid-js/web"; import "./styles.css"; function App() { return ( <div class="app-container"> <p>Just some text inside a div that has a restricted size.</p> <Portal> <div class="popup"> <h1>Popup</h1> <p>Some text you might need for something or other.</p> </div> </Portal> </div> ); } render(() => <App />, document.getElementById("app")); 复制代码
styles.css
.app-container { width: 200px; height: 100px; overflow: hidden; } .popup { position: relative; z-index: 2; background: #ddd; padding: 1rem; min-height: 200px; width: 200px; } .popup::after { content: " "; position: absolute; bottom: 100%; left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: transparent transparent #ddd transparent; } 复制代码
效果是这样的:
ErrorBoundary
ErrorBoundary
是一个可以捕获子组件任何未知产生的JavaScript错误,并显示发生错误的报错信息,举个🌰
import { render } from "solid-js/web"; import { ErrorBoundary } from "solid-js"; const Broken = (props) => { throw new Error("Oh No"); return <>Never Getting Here</> } function App() { return ( <> <div>Before</div> <ErrorBoundary fallback={(err) => err}> <Broken /> </ErrorBoundary> <div>After</div> </> ); } render(() => <App />, document.getElementById("app")); 复制代码
效果如下: