亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文詳解preact的高性能狀態(tài)管理Signals

 更新時間:2022年09月13日 10:49:33   作者:前端科代表張繼科  
這篇文章主要介紹了一文詳解preact的高性能狀態(tài)管理Signals,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的朋友可以參考一下

前言

Signals是一種用來描述狀態(tài)的方式,可以確保應(yīng)用程序保持快速,無論應(yīng)用程序變得多么的復(fù)雜。Signals基于響應(yīng)式原則,提供了優(yōu)秀的開發(fā)者工效學(xué),并且針對虛擬DOM優(yōu)化做了獨特實現(xiàn)。

Signals的核心在于,一個signal就是一個具有具有.value屬性的對象,并持有一些值。當(dāng)signal的value值發(fā)生變化時,從一個組件中訪問signal的value屬性會自動更新該組件。

除了直截了當(dāng)和易于編寫之外,無論你的應(yīng)用程序中有多少個組件,都還能確保狀態(tài)更新保持快速。Signals默認(rèn)是很快的,在幕后自動為你優(yōu)化更新性能。

run in repl

import { render } from "preact";

import { signal, computed } from "@preact/signals";
const count = signal(0);
const double = computed(() => count.value * 2);

function Counter() {
  return (
    <button onClick={() => count.value++}>
      {count} x 2 = {double}
    </button>
  );
}
render(<Counter />, document.getElementById("app"));

Signals可以在組件內(nèi)部或者組件外部使用,它跟hooks不一樣。Signals也可以和hooks、類組件一起使用,所以你可以帶著你現(xiàn)有的知識,按照你自己的節(jié)奏一步一步地引入它們。在一些組件中嘗試它們,并隨著時間的推移逐漸采用Signals。

哦順便說一下,我們一直堅持我們的初心,為大家提供盡可能小的類庫。在Preact中使用signals最終只給你的軟件包體積增加了1.6KB

如果你想跳過直接進(jìn)入學(xué)習(xí),請到我們的文檔中去深入了解signals。

signals解決了哪些問題?

在過去的幾年里,我們在各種應(yīng)用程序和團(tuán)隊中工作,從小型創(chuàng)業(yè)公司到有數(shù)百名開發(fā)人員同時投入的巨石應(yīng)用。在這段時間里,核心團(tuán)隊的每個人都注意到了應(yīng)用程序狀態(tài)管理方式中反復(fù)出現(xiàn)的問題。

為了解決這些問題,我們提出了一些神奇的解決方案,但是即使是最好的解決方案,也需要手動集成到框架中。因此,我們看到了開發(fā)者對采用這些解決方案時的猶豫不決,而更傾向于使用框架原本提供的狀態(tài)來構(gòu)建。

我們將Signals打造成一個引人注目的解決方案,將最佳性能、開發(fā)人員的工效學(xué)與框架的無縫集成結(jié)合起來。

全局狀態(tài)的斗爭

應(yīng)用程序的狀態(tài)一開始時通常是小而簡單的,也許是幾個簡單的useState hooks。隨著應(yīng)用程序的迭代,越來越多的組件需要訪問相同的狀態(tài),這些狀態(tài)最終被提升到一個共同的祖先組件上面。這種模式會重復(fù)多次,直到大部分的狀態(tài)最終都在組件樹的根組件附近。

這種場景給傳統(tǒng)的基于虛擬DOM的框架帶來了挑戰(zhàn),它們必須更新受狀態(tài)失效影響的整個樹。從本質(zhì)上來講,渲染性能是該樹中組件數(shù)量的函數(shù)。我們可以通過使用memo或者useMemo對組件樹的部分進(jìn)行記憶備忘來解決這個問題,這樣框架就會收到相同的對象。當(dāng)沒有變化時,可以讓框架跳過渲染樹的某些部分。

雖然這在理論上聽起來很合理,但是實際情況往往要混亂得多。在實踐過程中,隨著代碼庫的增長,很難確定這些優(yōu)化應(yīng)該放在哪里。通常,即使是用心良苦的記憶優(yōu)化也會因為不穩(wěn)定的依賴值而變得無效。由于hooks沒有可以分析的明確的依賴樹,所以工具不能幫助開發(fā)者診斷為什么依賴是不穩(wěn)定的。

上下文混亂

另一個常見的解決方法就是將狀態(tài)放到上下文中。這樣就可以通過跳過上下文提供者和消費者之間的組件渲染來實現(xiàn)短路渲染。但是有一個問題:只有傳遞給上下文提供者的值可以被更新,而且只能作為一個整體。更新通過上下文暴露的對象上的屬性并不能更新該上下文的消費者--細(xì)化更新是不可能的。解決這個問題的可行選項是將狀態(tài)分割成多個上下文,或者在上下文對象的任何屬性發(fā)生變化時通過克隆它來使其失效。

將狀態(tài)值轉(zhuǎn)移到上下文中,起初似乎是一個值得考慮的權(quán)衡,但是為了共享值而增加組件樹的大小,最終其弊端成為一個問題。業(yè)務(wù)邏輯最終不可避免地依賴于多個上下文值,這可能會迫使它在組件樹中的特定位置實現(xiàn)。在樹的中間添加一個訂閱上下文的組件是很昂貴的,因為它減少了更新上下文時可以跳過的組件數(shù)量。更重要的是,訂閱者下面的任何組件現(xiàn)在必須重新渲染。解決這個問題的唯一方法是大量使用記憶化,這又讓我們回到了記憶化固有的問題上。

尋找更好的方式來管理狀態(tài)

我們又回到了尋找下一代狀態(tài)基元的繪圖板上。我們想創(chuàng)造一些能解決當(dāng)前解決方案中問題的東西。手動進(jìn)行框架集成、過渡依賴記憶化、對上下文的次優(yōu)使用以及缺乏可編程的可觀察性,這些都讓人感覺很落后。

開發(fā)者需要考慮選擇加入這些策略的性能。如果我們能扭轉(zhuǎn)這種情況,提供一個默認(rèn)快速的系統(tǒng),使最佳性能成為你必須努力選擇的東西,那會怎么樣?

我們對這些問題的答案是Signals。這是一個默認(rèn)快速的系統(tǒng),不需要在你的應(yīng)用程序中使用記憶化或其它技巧。Signals提供了細(xì)粒度狀態(tài)更新的好處,無論該狀態(tài)是全局的、通過props或者上下文傳遞的,還是在某個組件的局部。

通往未來的Signals

Signals背后的主要思想是,我們不是通過組件樹傳遞一個值,而是傳遞一個包含該值的signal對象(類似于ref)。當(dāng)一個signal的value屬性改變時,signal本身保持不變。因此,signal可以被更新而不需要重新渲染它們所經(jīng)過的組件,因為組件看到的是signal而不是它的值。這讓我們跳過了渲染組件的昂貴工作,并立即跳到樹中實際訪問signal的value屬性的特定組件。

我們正在運用這樣一個事實:一個應(yīng)用程序的狀態(tài)圖通常比它的組件樹要淺得多。這樣可以做出更快的渲染,因為與組件樹相比,更新狀態(tài)圖所需的工作量要少得多。這種差異在瀏覽器中測量時最為明顯--下面的截圖顯示了同一個應(yīng)用程序兩次測量的DevTools Profiler軌跡:第一次使用hooks,第二次使用Signals。

signals版本大大優(yōu)于任何傳統(tǒng)的基于虛擬DOM的框架的更新機制。在我們測試的一些應(yīng)用程序中,signals的速度非???,以至于在火焰圖中根本難以找到它們。

signals的性能是翻轉(zhuǎn)的:signals不是通過記憶化或者選擇器來選擇性能,signals默認(rèn)就是快速的。有了signals,性能就可以選擇性不考慮(之前不使用signals)。

為了達(dá)到這樣的性能水平,signals是建立在這些關(guān)鍵原則之上的:

  • 默認(rèn)情況下是懶惰的:只觀察和更新目前在某處被使用到的signal--斷開連接的signal不會影響性能。
  • 最佳更新:如果一個signal的value沒有被修改,使用該signal的value的組件和effects就不會被更新,即使該signal的依賴關(guān)系已經(jīng)發(fā)生改變。
  • 最佳的依賴性跟蹤:框架為你跟蹤所有東西所依賴的signal--沒有像鉤子那樣的依賴性數(shù)組。
  • 直接訪問:在組件中訪問一個signal的value會自動訂閱更新,不再需要選擇器或者h(yuǎn)ook。

這些原則使得signal很適合廣泛的使用場景,甚至與渲染用戶界面無關(guān)的場景。

將signals帶入Preact

在確認(rèn)了正確的狀態(tài)基元后,我們開始將其與Preact進(jìn)行連接。我們一直都很喜歡hooks,因為它們可以直接在組件中使用。與第三方狀態(tài)管理解決方案相比,這是一個具備人性化的優(yōu)勢,后者通常依靠“選擇器”函數(shù)或?qū)⒔M件包裹在一個特殊的函數(shù)中來訂閱更新。

// Selector based subscription :(
function Counter() {
  const value = useSelector(state => state.count);
  // ...
}

// Wrapper function based subscription :(
const counterState = new Counter();

const Counter = observe(props => {
  const value = counterState.count;
  // ...
});

這兩種方法都不能讓我們感到滿意。選擇器的方法需要將所有的狀態(tài)訪問包裹在選擇器中,這對于復(fù)雜的或者嵌套的狀態(tài)來說變得很繁瑣。在函數(shù)中包裝組件的方法需要手工來包裝組件,這就帶來了一系列的問題,比如缺少組件名稱和靜態(tài)屬性。

在過去的幾年里,我們有機會與許多開發(fā)者密切合作。一個共同的掙扎,特別是對于那些剛接觸(p)react的人來說,像選擇器和包裝器這樣的概念是額外的范式,必須在感覺到每個狀態(tài)管理解決方案有成效之前學(xué)會。

理想情況下,我們不需要知道選擇器或包裝器函數(shù),可以直接訪問組件中的狀態(tài):

// 假設(shè)這是一個全局狀態(tài),整個應(yīng)用程序可以訪問:
let count = 0;

function Counter() {
 return (
   <button onClick={() => count++}>
     value: {count}
   </button>
 );
}

上面的代碼很清晰,很容易理解發(fā)生了什么,但是不幸的是,它并沒有發(fā)揮作用。當(dāng)點擊按鈕時,組件并沒有更新,因為沒有辦法知道count已經(jīng)改變。

我們無法將這個場景從我們的腦海中抹去。我們能做些什么來使如此清晰的模型成為現(xiàn)實呢?我們開始使用preact的可插拔渲染器,對各種想法和實現(xiàn)進(jìn)行原型設(shè)計。功夫不負(fù)有心人,我們最終找到了一個實現(xiàn)的方法:

// 假設(shè)這是一個全局狀態(tài),整個應(yīng)用程序可以訪問:
const count = signal(0);

function Counter() {
 return (
   <button onClick={() => count.value++}>
     Value: {count.value}
   </button>
 );
}

沒有選擇器,沒有封裝函數(shù),什么都沒有。訪問signal的value就足以讓組件知道它需要在該signal的value發(fā)生變化時進(jìn)行更新。在幾個應(yīng)用程序中測試了這個原型后,很明顯我們發(fā)現(xiàn)了一些事情。這樣寫代碼感覺很直觀,而且不需要任何精神體操來保持事物的最佳狀態(tài)。

我們的代碼可以跑得更快嗎?

我們本可以在這里停下來,按原來這樣發(fā)布signals,但這是Preact團(tuán)隊:我們需要看看我們能把Preact的集成推動到什么程度。在上面的Counter例子中,count的value只是用來顯示文本,這確實不應(yīng)該去重新渲染整個組件。如果我們不在signal的value變化時自動重新渲染組件,而只重新渲染文本呢?更妙的是,如果我們完全繞過虛擬DOM,直接在DOM中更新文本,會怎么樣?

const count = signal(0);

// Instead of this:
<p>Value: {count.value}</p>

// … we can pass the signal directly into JSX:
<p>Value: {count}</p>

// … or even passing them as DOM properties:
<input value={count} />

所以,是的,我們也這樣做了。你可以在通常使用字符串的任何地方將signal直接傳入JSX。signal的value將被呈現(xiàn)為文本,當(dāng)signal發(fā)生變化時,它將自動更新自己。這也適用于props。

下一步

如果你很好奇并想直接進(jìn)入查看,請到我們的文檔中查看signals。我們很樂意聽到你將如何使用它們。

請記住,不要急于切換到signals。hooks將繼續(xù)被支持,而且它們與signals一起使用也很好!我們建議逐漸嘗試signals。我們建議逐漸嘗試使用signals,從一些組件開始,以適應(yīng)這些概念。

到此這篇關(guān)于一文詳解preact的高性能狀態(tài)管理Signals的文章就介紹到這了,更多相關(guān)preact狀態(tài)管理Signals內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論