close
介紹

SolidJS 一個用於構建用戶界面,簡單高效、性能卓越的 JavaScript 庫。

性能-始終在UI速度和內存利用率基準測試中名列前茅
強大-可組合的響應式原語與 JSX 的靈活性相結合。
實用-合理且量身定製的 API 使開發變得有趣而簡單。
生產力-人體工程化設計和熟悉程度使得構建簡單或複雜的東西變得輕而易舉。

專注於性能

性能僅次於原生JS

一個簡單的例子import{render}from"solid-js/web";constApp=()=><div>hellosolidjs!</div>render(()=><App/>,document.getElementById("app"))

寫過react的應該很熟悉這段代碼,jsx片段,render函數。會讓你感覺既熟悉又現代。

響應式createSignalimport{render}from"solid-js/web";import{createSignal}from"solid-js";functionCounter(){const[count,setCount]=createSignal(0);setInterval(()=>setCount(count()+1),1000);return<div>{count()}</div>;}render(()=><Counter/>,document.getElementById('app'));

signal是solid中基本的響應單元,createSignal類似react中的useState,傳遞給createSignal調用的參數是初始值,createSignal返回一個兩個==函數==的數組,第一個getter,第二個是setter,第一個返回的值是一個getter而不是一個值,使用的時候需要調用,框架攔截讀取值的任何位置來進行自動跟蹤,從而響應式更新,所以調用getter的位置很重要,和react不同的是,例如setState觸發更新,react會生成Fiber樹,進行diff算法,最後執行dom操作。solid則是直接調用編譯好的dom操作方法,沒有虛擬dom比較。

createEffectimport{render}from'solid-js/web';import{createSignal,createEffect}from'solid-js';functionCounter(){const[count,setCount]=createSignal(0);createEffect(()=>{console.log('countis:',count())})return<buttononClick={()=>setCount(count()+1)}>Clickme</button>;}render(()=><Counter/>,document.getElementById('app'));

createEffect接收一個函數,監聽其執行情況,createEffect會自動訂閱在執行期間讀取的所有Signal,並在Signal值之一發生改變的時候,重新運行此函數。count更改的時候,createEffect函數就會運行,從而點擊一次,就打印一次結果。類似react的useEffect

useEffect(()=>{/*....*/},[count])衍生Signalimport{render}from"solid-js/web";import{createSignal}from"solid-js";functionCounter(){const[count,setCount]=createSignal(0);constdoubleCount=()=>count()*2setInterval(()=>setCount(count()+1),1000);return<div>Count:{doubleCount()}</div>;}render(()=><Counter/>,document.getElementById('app'));

這次沒有直接使用count(), 而是使用了doubleCount函數包了一層count() * 2, 日誌正常打印2倍的count(), 意味着任何包裝count()的函數,都是一個Signal,訪問JSX中的js表達式也是,只要訪問一個signal,就會觸發更新。

Props

Props是組件執行的時候傳進來的對象,Props是只讀的,並且具備對象getter的響應性的,但是響應性,只能通過props.propsName的形式來訪問,才能被追蹤到。不能解構props,解構就會脫離追蹤範圍而失去響應。

默認Props//greeting.jsximport{mergeProps}from"solid-js";exportdefaultfunctionGreeting(props){return<h3>{props.greeting||"Hi"}{props.name||"John"}</h3><!--const{greeting,name}=props--><!--return<h3>{greeting||'Hi'}{name||'John'}</h3>-->}//main.jsximport{render}from"solid-js/web";import{createSignal}from"solid-js";importGreetingfrom"./greeting";functionApp(){const[name,setName]=createSignal();return<><Greetinggreeting="Hello"/><Greetingname="Jeremy"/><Greetingname={name()}/><buttononClick={()=>setName("Jarod")}>SetName</button></>;}render(()=><App/>,document.getElementById('app'));

當然SolidJS也是提供了一個工具函數mergeProps,使得具有響應性。

//greeting.jsximport{mergeProps}from"solid-js";exportdefaultfunctionGreeting(props){constmerged=mergeProps({greeting:"Hi",name:"John"},props)return<h3>{merged.greeting}{merged.name}</h3>}分離Props

合併props用到的地方很少,我們經常用到的是解構組件傳進來的props,然後將其他props分離出來,再傳遞下去。

//greeting.jsxexportdefaultfunctionGreeting(props){const{greeting,name,...others}=props;return<h3{...others}>{greeting}{name}</h3>}

直接解構分離,設置name的時候,不會更新,也就是解構的時候失去了響應性。但是solid為我們提供了分離props的函數splitProps,來保持響應性。

//greeting.jsxexportdefaultfunctionGreeting(props){const[local,others]=splitProps(props,["greeting","name"]);return<h3{...others}>{local.greeting}{local.name}</h3>}Store內嵌式響應

solid可以獨立處理嵌套更新,是因為它提供了一細粒度響應式,也就是哪裡需要更新,就更新哪裡,指哪打哪。

import{render}from"solid-js/web";import{For,createSignal}from"solid-js";constApp=()=>{const[todos,setTodos]=createSignal([])letinput;lettodoId=0;constaddTodo=(text)=>{//setTodos([...todos(),{id:++todoId,text,completed:false}]);const[completed,setCompleted]=createSignal(false)setTodos([...todos(),{id:++todoId,text,completed,setCompleted}]);}consttoggleTodo=(id)=>{//setTodos(todos().map((todo)=>(//todo.id!==id?todo:{...todo,completed:!todo.completed}//)));constindex=todos().findIndex((t)=>t.id===id);consttodo=todos()[index];if(todo)todo.setCompleted(!todo.completed())}return(<><div><inputref={input}/><buttononClick={(e)=>{if(!input.value.trim())return;addTodo(input.value);input.value="";}}>AddTodo</button></div><Foreach={todos()}>{(todo)=>{const{id,text}=todo;console.log(`Creating${text}`)return<div><inputtype="checkbox"checked={todo.completed}onchange={[toggleTodo,id]}/><spanstyle={{"text-decoration":todo.completed?"line-through":"none"}}>{text}</span></div>}}</For></>);};render(App,document.getElementById("app"));

兩種方式,第一種是追蹤todos()更新,效果是新增的時候會觸發渲染,等到toggleTodo的時候還是會觸發渲染,第二種是,嵌套更新,追蹤對象的屬性completed(), 新增的時候追蹤todos()變化,渲染dom,等到toggleTodo的時候,只會觸發數據更新,dom已經渲染了,沒有必要再次渲染了。

創建store

store是代理對象,屬性可以被跟蹤,那麼是不是可以實現內嵌式響應,createStore接收一個初始值,返回一個類似於signal的讀/寫的兩個元素,第一個是元素只讀的store代理,第二個是setter函數。

import{render}from"solid-js/web";import{For,createSignal}from"solid-js";import{createStore}from"solid-js/store";constApp=()=>{letinput;lettodoId=0;const[store,setStore]=createStore({todos:[]});constaddTodo=(text)=>{setStore('todos',(todos)=>[...todos,{id:++todoId,text,completed:false}]);};consttoggleTodo=(id)=>{setStore('todos',(t)=>t.id===id,'completed',(completed)=>!completed);};return(<><div><inputref={input}/><buttononClick={(e)=>{if(!input.value.trim())return;addTodo(input.value);input.value="";}}>AddTodo</button></div><Foreach={store.todos}>{(todo)=>{const{id,text}=todo;console.log(`Creating${text}`)return<div><inputtype="checkbox"checked={todo.completed}onchange={[toggleTodo,id]}/><spanstyle={{"text-decoration":todo.completed?"line-through":"none"}}>{text}</span></div>}}</For></>);};render(App,document.getElementById("app"));內置組件Show

條件渲染

import{render}from'solid-js/web';import{createSignal,Show}from'solid-js';functionApp(){const[loggedIn,setLoggedIn]=createSignal(false);consttoggle=()=>setLoggedIn(!loggedIn())return(<Showwhen={loggedIn()}fallback={()=><buttononClick={toggle}>Login</button>}><buttononClick={toggle}>Logout</button></Show>);}render(()=><App/>,document.getElementById('app'))For

循環遍歷,數據是固定的,index是可追蹤的signal,涉及到dom移動的時候,不會觸發重新創建dom。只是index獨立更新,數據並不會更新。

import{render}from'solid-js/web';import{createSignal,For}from'solid-js';functionApp(){const[cats,setCats]=createSignal([{id:'J---aiyznGQ',name:'KeyboardCat'},{id:'z_AbfPXTKms',name:'Maru'},{id:'OUtn3pvWmpg',name:'HenriTheExistentialCat'}]);return(<ul><Foreach={cats()}>{(cat,i)=>(<li><atarget="_blank"href={`https://www.youtube.com/watch?v=${cat.id}`}>{i()+1}:{cat.name}</a></li>)}</For></ul>);}render(()=><App/>,document.getElementById('app'))Index

Index和For不同的是,Index是數據項是signal,索引是固定的。

import{render}from'solid-js/web';import{createSignal,Index}from'solid-js';functionApp(){const[cats,setCats]=createSignal([{id:'J---aiyznGQ',name:'KeyboardCat'},{id:'z_AbfPXTKms',name:'Maru'},{id:'OUtn3pvWmpg',name:'HenriTheExistentialCat'}]);return(<ul><Indexeach={cats()}>{(cat,i)=>(<li><atarget="_blank"href={`https://www.youtube.com/watch?v=${cat().id}`}>{i+1}:{cat().name}</a></li>)}</Index></ul>);}render(()=><App/>,document.getElementById('app'))Switch

處理互斥條件

import{render}from"solid-js/web";import{createSignal,Show,Switch,Match}from"solid-js";functionApp(){const[x]=createSignal(7);return(//使用show實現<!--<Show--><!--when={x()>10}--><!--fallback={--><!--<Show--><!--when={5>x()}--><!--fallback={<p>{x()}isbetween5and10</p>}--><!-->--><!--<p>{x()}islessthan5</p>--><!--</Show>--><!--}--><!-->--><!--<p>{x()}isgreaterthan10</p>--><!--</Show>-->//使用Switch實現<Switchfallback={<p>{x()}isbetween5and10</p>}><Matchwhen={x()>10}><p>{x()}isgreaterthan10</p></Match><Matchwhen={5>x()}><p>{x()}islessthan5</p></Match></Switch>);}render(()=><App/>,document.getElementById("app"));Dynamic

比<Show> 或 <Switch> 組件更簡練

import{render,Dynamic}from"solid-js/web";import{createSignal,Switch,Match,For}from"solid-js";constRedThing=()=><strongstyle="color:red">RedThing</strong>;constGreenThing=()=><strongstyle="color:green">GreenThing</strong>;constBlueThing=()=><strongstyle="color:blue">BlueThing</strong>;constoptions={red:RedThing,green:GreenThing,blue:BlueThing}functionApp(){const[selected,setSelected]=createSignal("red");return(<><selectvalue={selected()}onInput={e=>setSelected(e.currentTarget.value)}><Foreach={Object.keys(options)}>{color=><optionvalue={color}>{color}</option>}</For></select><!--<Switchfallback={<BlueThing/>}>--><!--<Matchwhen={selected()==="red"}><RedThing/></Match>--><!--<Matchwhen={selected()==="green"}><GreenThing/></Match>--><!--</Switch>--><Dynamiccomponent={options[selected()]}/></>);}render(()=><App/>,document.getElementById("app"));生命周期onMount

掛載

solid有極少的生命周期,請求數據,以及一些邏輯都寫到這個地方,這個函數只會被調用一次。

import{render}from"solid-js/web";import{createSignal,onMount,For}from"solid-js";import"./styles.css";functionApp(){const[photos,setPhotos]=createSignal([]);onMount(async()=>{constres=awaitfetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);setPhotos(awaitres.json());});return<><h1>Photoalbum</h1><divclass="photos"><Foreach={photos()}fallback={<p>Loading...</p>}>{photo=><figure><imgsrc={photo.thumbnailUrl}alt={photo.title}/><figcaption>{photo.title}</figcaption></figure>}</For></div></>;}render(()=><App/>,document.getElementById('app'));onCleanup

卸載時

import{render}from"solid-js/web";import{createSignal,onCleanup}from"solid-js";functionCounter(){const[count,setCount]=createSignal(0);consttimer=setInterval(()=>setCount(count()+1),1000);onCleanup(()=>clearInterval(timer));return<div>Count:{count()}</div>;}render(()=><Counter/>,document.getElementById('app'));總結
沒有虛擬DOM。
響應式跟蹤更新,指哪打哪。
支持jsx
寫法類似react,上手容易
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()