SolidJS:一款高性能的響應式JavaScript UI庫
Solid是一個用於創建用戶界面的響應式JavaScript庫,它無需虛擬DOM。它將模板編譯成真正的DOM節點,並將更新包裝在細粒度的反應中,因此當狀態更新時,只有相關的代碼才會運行。
這種方式使得編譯器可以優化初始渲染,運行時可以優化更新。這種對性能的關注使其成為最受好評的JavaScript框架之一。
我對此很好奇,想嘗試一下,所以我花了一些時間創建了一個小型待辦事項應用程序,來探索這個框架如何處理渲染組件、更新狀態、設置存儲等等。
如果您迫不及待地想查看最終代碼和結果,請查看最終演示: [此處應插入最終演示鏈接,原文未提供]
與大多數框架一樣,我們可以從安裝npm包開始。要將該框架與JSX一起使用,請運行:
npm install solid-js babel-preset-solid
然後,我們需要將babel-preset-solid添加到我們的Babel、webpack或Rollup配置文件中:
"presets": ["solid"]
或者,如果您想搭建一個小型應用程序,您也可以使用他們的模板之一:
# 從Solid模板創建一個小型應用程序npx degit solidjs/templates/js my-app # 更改到創建的項目目錄cd my-app # 安裝依賴項npm i # 或yarn 或pnpm # 啟動開發服務器npm run dev
支持TypeScript,如果您想啟動一個TypeScript項目,請將第一個命令更改為npx degit solidjs/templates/ts my-app
。
渲染組件的語法類似於React.js,因此可能看起來很熟悉:
import { render } from "solid-js/web"; const HelloMessage = props =><div> Hello {props.name}</div> ; render( () =><hellomessage name="Taylor"></hellomessage> , document.getElementById("hello-example") );
我們需要先導入render函數,然後創建一個帶有文本和prop的div,並調用render,傳入組件和容器元素。
這段代碼隨後被編譯成真正的DOM表達式。例如,上面的代碼示例,一旦被Solid編譯,看起來像這樣:
import { render, template, insert, createComponent } from "solid-js/web"; const _tmpl$ = template(`<div> Hello</div> `); const HelloMessage = props => { const _el$ = _tmpl$.cloneNode(true); insert(_el$, () => props.name); return _el$; }; render( () => createComponent(HelloMessage, { name: "Taylor" }), document.getElementById("hello-example") );
Solid Playground非常酷,它顯示Solid有不同的渲染方式,包括客戶端、服務器端和帶有水合的客戶端。
Solid使用一個名為createSignal
的hook,它返回兩個函數:一個getter和一個setter。如果您習慣使用像React.js這樣的框架,這可能看起來有點奇怪。您通常期望第一個元素是值本身;但是,在Solid中,我們需要顯式調用getter來攔截讀取值的位置,以便跟踪其更改。
例如,如果我們正在編寫以下代碼:
const [todos, addTodos] = createSignal([]);
記錄todos
不會返回值,而是一個函數。如果我們想使用該值,我們需要調用該函數,例如todos()
。
對於一個小的待辦事項列表,這將是:
import { createSignal } from "solid-js"; const TodoList = () => { let input; const [todos, addTodos] = createSignal([]); const addTodo = value => { return addTodos([...todos(), value]); }; return ( <h1>To do list:</h1> <input type="text" ref="{el"> input = el} /> <button onclick="{()"> addTodo(input.value)}>Add item</button>
上面的代碼示例將顯示一個文本字段,單擊“添加項目”按鈕後,將使用新項目更新todos並在列表中顯示它。
這看起來可能與使用useState
非常相似,那麼使用getter有什麼不同呢?考慮以下代碼示例:
console.log("Create Signals"); const [firstName, setFirstName] = createSignal("Whitney"); const [lastName, setLastName] = createSignal("Houston"); const [displayFullName, setDisplayFullName] = createSignal(true); const displayName = createMemo(() => { if (!displayFullName()) return firstName(); return `${firstName()} ${lastName()}`; }); createEffect(() => console.log("My name is", displayName())); console.log("Set showFullName: false "); setDisplayFullName(false); console.log("Change lastName "); setLastName("Boop"); console.log("Set showFullName: true "); setDisplayFullName(true);
運行上面的代碼將得到:
<code>Create Signals My name is Whitney Houston Set showFullName: false My name is Whitney Change lastName Set showFullName: true My name is Whitney Boop</code>
需要注意的主要一點是,在設置新的lastName後,“My name is...”沒有被記錄。這是因為此時沒有任何內容正在監聽lastName()
的更改。只有當displayFullName()
的值更改時, displayName()
的新值才會被設置,這就是為什麼當setShowFullName
被設置為true時,我們可以看到新的lastName被顯示。
這為我們提供了一種更安全的方式來跟踪值的更新。
在最後一個代碼示例中,我介紹了createSignal
,還有一些其他的原語: createEffect
和createMemo
。
createEffect
跟踪依賴項,並在每次依賴項發生更改的渲染後運行。
// 不要忘記首先使用'import { createEffect } from "solid-js";' 導入它const [count, setCount] = createSignal(0); createEffect(() => { console.log("Count is at", count()); });
每次count()
的值發生更改時,都會記錄“Count is at...”
createMemo
創建一個只讀信號,每當執行的代碼的依賴項更新時,它都會重新計算其值。當您想要緩存一些值並訪問它們而無需重新評估它們(直到依賴項更改)時,可以使用它。
例如,如果我們想顯示一個計數器100次並在單擊按鈕時更新值,使用createMemo
將允許重新計算僅在每次點擊時發生一次:
function Counter() { const [count, setCount] = createSignal(0); // 不用createMemo包裝counter會調用100次// const counter = () => { // return count(); // } // 用createMemo包裝counter,每次更新只調用一次// 不要忘記首先使用'import { createMemo } from "solid-js";' 導入它const counter = createMemo(() => count()); return ( <div> <button onclick="{()"> setCount(count() 1)}>Count: {count()}</button> <div>1. {counter()}</div> <div>2. {counter()}</div> <div>3. {counter()}</div> <div>4. {counter()}</div> </div> ); }
Solid公開了幾個生命週期方法,例如onMount
、 onCleanup
和onError
。如果我們希望某些代碼在初始渲染後運行,我們需要使用onMount
:
// 不要忘記首先使用'import { onMount } from "solid-js";' 導入它onMount(() => { console.log("I mounted!"); });
onCleanup
類似於React中的componentDidUnmount
——它在響應式作用域重新計算時運行。
onError
在最近的子作用域中發生錯誤時執行。例如,當數據獲取失敗時,我們可以使用它。
要為數據創建存儲,Solid公開了createStore
,其返回值是一個只讀代理對象和一個setter函數。
例如,如果我們將我們的待辦事項示例更改為使用存儲而不是狀態,它將如下所示:
const [todos, addTodos] = createStore({ list: [] }); createEffect(() => { console.log(todos.list); }); onMount(() => { addTodos('list', (list) => [...list, { item: "a new todo item", completed: false }]); });
上面的代碼示例將首先記錄一個帶有空數組的代理對象,然後記錄一個帶有數組的代理對象,該數組包含對象{item: "a new todo item", completed: false}
。
需要注意的是,如果不訪問其屬性,則無法跟踪頂級狀態對象——這就是為什麼我們記錄todos.list
而不是todos
的原因。
如果我們只在createEffect
中記錄todos
,我們將看到列表的初始值,但不會看到在onMount
中進行更新後的值。
要更改存儲中的值,我們可以使用在使用createStore
時定義的設置函數來更新它們。例如,如果我們想將待辦事項列表項更新為“已完成”,我們可以通過這種方式更新存儲:
const [todos, setTodos] = createStore({ list: [{ item: "new item", completed: false }] }); const markAsComplete = text => { setTodos( "list", (i) => i.item === text, "completed", (c) => !c ); }; return ( <button onclick="{()"> markAsComplete("new item")}>Mark as complete</button> );
為了避免在使用.map()
等方法時在每次更新時浪費性地重新創建所有DOM節點,Solid允許我們使用模板助手。
其中一些可用,例如For
用於循環遍歷項目, Show
用於有條件地顯示和隱藏元素, Switch
和Match
用於顯示與特定條件匹配的元素,等等!
以下是一些顯示如何使用它們的示例:
<for each="{todos.list}" fallback="{<div"> Loading... }> {(item) =><div> {item}</div> } </for> <show when="{todos.list[0]?.completed}" fallback="{<div"> Loading... }> <div>1st item completed</div> </show> <switch fallback="{<div"> No items }> <match when="{todos.list[0]?.completed}"><completedlist></completedlist></match> <match when="{!todos.list[0]?.completed}"><todolist></todolist></match> </switch>
這是對Solid基礎知識的快速介紹。如果您想試用它,我創建了一個入門項目,您可以通過單擊下面的按鈕將其自動部署到Netlify並克隆到您的GitHub!
[此處應插入部署到Netlify的按鈕,原文未提供] 該項目包括Solid項目的默認設置,以及我在這篇文章中提到的基本概念的示例待辦事項應用程序,以幫助您入門!
這個框架比我在這裡介紹的要多得多,所以請隨意查看文檔以了解更多信息!
以上是固體JavaScript庫簡介的詳細內容。更多資訊請關注PHP中文網其他相關文章!