SolidJS硬氣的說:我比React還react

你們好,我是卡頌。html

最近刷推時,有個老哥常常出如今前端框架相關推文下。前端

一副憨厚的樣貌

我想:「老哥你哪位?」react

一查,原來是個框架做者,做品叫SolidJSgit

翻翻框架介紹,這句話成功吸引個人注意:github

支持現代前端特性,例如:JSX, Fragments, Context, Portals, Suspense, Streaming SSR, Progressive Hydration, Error Boundaries和Concurrent Renderingweb

我琢磨您不會是React在逃公主吧?這不能說和React相似,只能說徹底同樣吧?算法

做爲傳統中國人,秉承來都來了思想,我試用了一天,又看了下源碼,結果發現這個框架真是個寶藏框架。前端框架

本文會比較SolidJSReact的異同,闡述他的獨特優點,看完後不知道你會不會和我發出一樣的感嘆:markdown

這簡直比React還react(react譯爲響應)閉包

相信看完本文後,不只能認識一個新框架,還能對React有更深的認識。

開整!

初看很類似

讓咱們從一個計數器的例子看看與React語法的差別:

import { render } from "solid-js/web";
import { createSignal } from "solid-js";

function Counter() {
  const [count, setCount] = createSignal(0);
  
  const increment = () => setCount(count() + 1);

  return (
    <button type="button" onClick={increment}> {count()} </button>
  );
}

render(() => <Counter />, document.getElementById("app"));
複製代碼

React不一樣的地方:

  • useState更名成createSignal

  • 獲取count狀態從React中直接使用count變爲經過方法調用,即:count()

難道僅僅是一個類React框架?

別急,讓咱們從編譯時運行時響應原理三方面來看看。

編譯時大不一樣

React的編譯時很,基本只是編譯JSX語法。

SolidJS則採用了相似Svelte的方案:在編譯時,將狀態更新編譯爲獨立的DOM操做方法。

這樣作有什麼好處?主要有兩點。

必定條件下的體積優點

你不須要爲你沒使用的代碼付出代價

使用React時,即便沒有用到Hooks,其代碼也會出如今最終編譯後的代碼中。

而在SolidJS中,未使用的功能不會出如今編譯後的代碼中。

舉個例子,上面計時器的例子中,編譯後的代碼有一行是這樣:

delegateEvents(["click"]);
複製代碼

這行代碼的目的是在document上註冊click事件代理。

若是在計時器中沒有使用onClick,那麼編譯後代碼中就不會有這一行。

有熱心網友對比了相似編譯時方案的SvelteReact之間源代碼編譯後代碼的體積差別。

其中橫軸表明源代碼體積,縱軸表明編譯後代碼體積,紅色線條表明Svelte,藍色表明React

可見,在臨界值(業務源代碼體積達到120kb)以前,編譯時方案有必定體積優點。

因爲SolidJS使用JSX描述視圖,比Svelte使用相似Vue的模版語法更靈活,因此在編譯時無法作到Svelte同樣的極致編譯優化,使得其相比Svelte運行時更重一點。

這爲他帶來了額外的好處:在真實項目(>120kb)中,SolidJS的代碼體積比Svelte小25%左右。

還真是,塞翁失馬?

更快的更新速度

咱們知道,在ReactVue中存在一層虛擬DOMReact中叫Fiber樹)。

每當發生更新,虛擬DOM會進行比較(Diff算法),比較的結果會執行不一樣的DOM操做(增、刪、改)。

SolidJSSvelte在發生更新時,能夠直接調用編譯好的DOM操做方法,省去了虛擬DOM比較這一步所消耗的時間。

舉個例子,上文的計時器,當點擊後,從觸發更新到視圖變化的調用棧以下:

觸發事件,更新狀態,更新視圖,一路調用走到底,清晰明瞭。

一樣的例子放到React中,調用棧以下:

左中右紅、綠、藍框調用棧分別對應:

  • 處理事件

  • 對比並生成Fiber

  • 根據對比結果執行DOM操做

可見,SolidJS的更新路徑比React短不少。

你問憑什麼?這還得從其特殊的響應原理聊起。

響應原理

假設有個狀態name,初始值爲KaSong。咱們但願根據name渲染一個div

SolidJS編譯後的代碼相似:

const [name, setName] = createSignal("KaSong");

const el = document.createElement("div");
createEffect(() => el.textContent = name());
複製代碼

其中createEffect相似ReactuseEffect

因爲其回調內依賴了name,因此當name改變後會觸發createEffect回調,改變el.textContent,形成DOM更新。

相似React的:

useEffect(() => {
  el.textContent = name;
}, [name])
複製代碼

首屏渲染結果:

<div>KaSong</div>
複製代碼

接下來,觸發更新:

setName("XiaoMing") 
複製代碼

更新後結果:

<div>XiaoMing</div>
複製代碼

爲何更新name後會觸發createEffect

這裏也沒有什麼黑魔法,就是訂閱發佈

createEffect回調依賴name,因此會訂閱name的變化。

因爲篇幅有限,實現細節咱下回細聊。

這裏的關鍵在於,SolidJS的狀態具備原子性

即狀態互相之間有依賴關係,他們造成局部的依賴圖。當改變一個狀態後,依賴圖中的其餘狀態也會改變。

createEffect中若是使用了這些依賴,就會訂閱他們的變化。

當狀態改變後,createEffect回調會執行,進而執行具體的DOM方法,更新視圖。

響應式更新,指哪打哪,李雲龍直呼內行。

有同窗會問,React不是這樣麼?

那我問你個問題:

爲何Hooks會有調用順序不能變的要求?

爲何useEffect回調會有閉包問題?

答案已經呼之欲出了:React只有在這些限制下才能實現響應式

辛勞苦幹React

有一個可能反直覺的知識:React並不關心哪一個組件觸發了更新。

React中,任何一個組件觸發更新(如調用this.setState),全部組件都會從新走一遍流程。由於須要構建一棵新的Fiber樹。

爲了減小無心義的renderReact內部有些優化策略用來判斷組件是否能夠複用上次更新的Fiber節點(從而跳過render)。

同時,也提供了不少API(好比:useMemoPureComponent...),讓開發者告訴他哪些組件能夠跳過render

若是說,SolidJS的更新流程像一個畫家,畫面中哪兒須要更新就往哪兒畫幾筆。

那麼React的更新流程像是一我的拿相機拍一張照片,再拿這張照片和上次拍的照片找不一樣,最後把不一樣的地方更新了。

總結

今天,咱們聊了SolidJSReact的差別,主要體如今三方面:

  • 編譯時

  • 運行時

  • 響應原理

不知道你喜歡這款:沒有Hooks順序限制、沒有useEffect閉包問題、沒有Fiber樹、比Reactreact的框架麼?

若是你問我選哪一個?固然,哪一個給工資高我用哪一個。

相關文章
相關標籤/搜索