新聞中心
Signal:更多前端框架的選擇

成都服務(wù)器托管,創(chuàng)新互聯(lián)提供包括服務(wù)器租用、成都服務(wù)器托管、帶寬租用、云主機(jī)、機(jī)柜租用、主機(jī)租用托管、CDN網(wǎng)站加速、域名與空間等業(yè)務(wù)的一體化完整服務(wù)。電話咨詢:13518219792
大家好,我卡頌。
最近,Angular?、Qwik?的作者「MI?KO HEVERY」發(fā)文表示Signal是前端框架的未來(lái)[1],并考慮在Angular中實(shí)現(xiàn)它。
在此之前,Vue?、Solid.js?、Preact?、Svelte?都已實(shí)現(xiàn)Signal?。實(shí)際上,signal并不是一個(gè)新概念,他還有很多別名,比如:
- 響應(yīng)式更新
- 細(xì)粒度更新
如果你了解過(guò)Vue?響應(yīng)式更新的實(shí)現(xiàn)原理,對(duì)Signal就不會(huì)陌生。
實(shí)際上,Signal?的技術(shù)在10年前Knockout框架中就有應(yīng)用。為什么這項(xiàng)技術(shù)正受到越來(lái)越多前端框架的青睞?
本文,讓我們一起探討下這個(gè)話題。
signal的本質(zhì)
signal?的本質(zhì),是將「對(duì)狀態(tài)的引用」以及「對(duì)狀態(tài)值的獲取」分離開(kāi)。這么說(shuō)可能有點(diǎn)抽象,讓我們先看一個(gè)非signal的例子。
以下是React中定義狀態(tài)的方式:
function App() {
const [state, dispatch] = useState(0);
return () => dispatch(state + 1)
}>{state}
}useState的返回值包括兩部分:
- state:狀態(tài)的值
- dispatch?:狀態(tài)的setter
可以發(fā)現(xiàn),state耦合了「對(duì)狀態(tài)的引用」以及「對(duì)狀態(tài)值的獲取」這兩個(gè)含義。
再來(lái)看一個(gè)signal?的例子。以下是同一個(gè)例子用Solid.js書(shū)寫(xiě)的樣子:
function App() {
const [getState, dispatch] = createSignal(0);
return () => dispatch(getState() + 1)
}>{getState()}
}createSignal的返回值包括兩部分:
- getState:對(duì)狀態(tài)的引用
- dispatch:狀態(tài)的setter
區(qū)別就體現(xiàn)在getState上,其中:
- getState是對(duì)狀態(tài)的引用
- getState()是對(duì)狀態(tài)值的獲取
也就是說(shuō),我們可以不必立刻獲取狀態(tài)的值,而是在需要的時(shí)候再獲?。丛谛枰獣r(shí)再執(zhí)行g(shù)etState())。
這么做有什么好處呢?如果我們?cè)谛枰臅r(shí)候再獲取狀態(tài)的值,就能感知當(dāng)前的上下文環(huán)境。
舉個(gè)很粗糙的例子,在下面的代碼中,組件實(shí)例(Component實(shí)例)在render時(shí)會(huì)將全局變量cpnContext指向自己:
let cpnContext = null;
class Component {
render() {
cpnContext = this;
// ...省略邏輯
}
}
那么在createSignal返回的getState方法內(nèi)部,可以獲取全局變量cpnContext來(lái)感知當(dāng)前處于哪個(gè)組件的渲染流程:
function createSignal() {
// ...省略邏輯
function getState() {
const curContext = cpnContext;
// ...
}
function dispatch {}
return [getState, dispatch]
}這么做的目的是建立「狀態(tài)變化」與「需要更新哪個(gè)組件」之間的聯(lián)系。
相比于React?,基于Signal實(shí)現(xiàn)的框架會(huì)有兩個(gè)優(yōu)勢(shì):
- 更好的細(xì)粒度更新性能
- 更好的DX(開(kāi)發(fā)者體驗(yàn))
更好的細(xì)粒度更新性能
由于Signal建立了狀態(tài)與組件之間的聯(lián)系,所以相比于React更有性能優(yōu)勢(shì)。
比如,在我的電腦上,用Svelte渲染1w個(gè)li,點(diǎn)擊某個(gè)li后改變他的內(nèi)容:
- items[item.id].name = 'change!'}>{item.name}
{#each items as item (item.id)}
{/each}
從點(diǎn)擊事件觸發(fā),到Svelte邏輯運(yùn)行完,再到瀏覽器重排重繪,總用時(shí)18.88ms,其中Svelte的邏輯執(zhí)行只花了9.5ms:
同樣的例子用React實(shí)現(xiàn),觸發(fā)點(diǎn)擊后總用時(shí)98.5ms,其中React的邏輯執(zhí)行了89.38ms:
在這個(gè)例子中,React?性能比Svelte差了一個(gè)數(shù)量級(jí)。之所以會(huì)有這樣的差異,很大一部分原因在于「Svlete在更新前就知道狀態(tài)變化時(shí)需要更新哪個(gè)組件」。
而這一切的源頭就在于Signal。
更好的DX
更好的開(kāi)發(fā)者體驗(yàn)主要體現(xiàn)在兩方面:
1、Signal感知上下文環(huán)境的能力減少了代碼心智負(fù)擔(dān)。
比如在React中,useEffect在使用時(shí)需要指明依賴的狀態(tài):
useEffect(() => {
// ...state1, state2變化后的邏輯
}, [state1, state2])如果采用Signal的實(shí)現(xiàn),狀態(tài)能感知到自己在useEffect上下文環(huán)境,可以自動(dòng)建立兩者之間的聯(lián)系,不用再擔(dān)心少寫(xiě)依賴狀態(tài)、閉包陷阱等問(wèn)題,減少心智負(fù)擔(dān)。
比如在Vue中,類似useEffect(僅僅是功能類似,兩者的用途其實(shí)是不同的)的watch,就不需要顯式指明依賴:
2、減少開(kāi)發(fā)者性能優(yōu)化的心智負(fù)擔(dān)
使用Signal的框架通常能獲得不錯(cuò)的運(yùn)行時(shí)性能,所以不需要額外的性能優(yōu)化API。反觀React,開(kāi)發(fā)者如果遇到性能問(wèn)題,需要手動(dòng)調(diào)用性能優(yōu)化API(比如React.memo、useMemo、PureComponent)。
總結(jié)
有以上這么多優(yōu)點(diǎn),難怪很多框架都使用了Signal?。那么React對(duì)Signal是什么態(tài)度呢?
React團(tuán)隊(duì)成員對(duì)此的觀點(diǎn)是:
- 有可能引入類似Signal的原語(yǔ)
- Signal?性能確實(shí)好,但他不太符合React的理念?
React的理念可以用一句話概括:「UI反映狀態(tài)在某一刻的快照」。
既然是快照,那就不是局部的,而是個(gè)整體概念。在React?中,狀態(tài)更新會(huì)引起整個(gè)應(yīng)用重新render?,就是對(duì)React快照理念的最好詮釋。
React?現(xiàn)階段的所有實(shí)現(xiàn)都是基于快照理念。所以,即使引入類似Signal?的原語(yǔ),可能也是類似Mobx這樣的上層實(shí)現(xiàn),而不是從底層重構(gòu)。
我個(gè)人比較傾向于認(rèn)為:React?團(tuán)隊(duì)承認(rèn)Signal的優(yōu)點(diǎn),但由于積重難返,而且現(xiàn)代設(shè)備的性能通常是過(guò)剩的,所以性能問(wèn)題并不是首要問(wèn)題。
如果這個(gè)觀點(diǎn)是正確的,那么React?可能會(huì)在開(kāi)發(fā)者體驗(yàn)(Signal的另一個(gè)優(yōu)點(diǎn))方面努努力。比如去年提出的RFC: useEvent[2]可能就是這方面的一次嘗試。
參考資料
[1]Signal是前端框架的未來(lái):https://www.builder.io/blog/usesignal-is-the-future-of-web-frameworks#code-use-ref-code-does-not-render。
[2]RFC: useEvent:https://github.com/reactjs/rfcs/pull/220。
本文題目:Signal:更多前端框架的選擇
網(wǎng)站路徑:http://www.dlmjj.cn/article/dhshcsg.html


咨詢
建站咨詢
