自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

簡單、好懂的Svelte實現(xiàn)原理

開發(fā) 前端
本文會圍繞一張流程圖和兩個Demo講解,正確的食用方式是用電腦打開本文,跟著流程圖、Demo一邊看、一邊敲、一邊學

[[434242]]

大家好,我卡頌。

Svelte問世很久了,一直想寫一篇好懂的原理分析文章,拖了這么久終于寫了。

本文會圍繞一張流程圖和兩個Demo講解,正確的食用方式是用電腦打開本文,跟著流程圖、Demo一邊看、一邊敲、一邊學。

讓我么開始吧。

Demo1

Svelte的實現(xiàn)原理如圖:

圖中Component是開發(fā)者編寫的組件,內(nèi)部虛線部分是由Svelte編譯器編譯而成的。圖中的各個箭頭是運行時的工作流程。

首先來看編譯時,考慮如下App組件代碼:

  1. <h1>{count}</h1> 
  2.  
  3. <script> 
  4.   let count = 0; 
  5. </script> 

完整代碼見Demo1 repl[1]

瀏覽器會顯示:

這段代碼經(jīng)由編譯器編譯后產(chǎn)生如下代碼,包括三部分:

  • create_fragment方法
  • count的聲明語句
  • class App的聲明語句
  1. // 省略部分代碼… 
  2. function create_fragment(ctx) { 
  3.   let h1; 
  4.  
  5.   return { 
  6.     c() { 
  7.       h1 = element("h1"); 
  8.       h1.textContent = `${count}`; 
  9.     }, 
  10.     m(target, anchor) { 
  11.       insert(target, h1, anchor); 
  12.     }, 
  13.     d(detaching) { 
  14.       if (detaching) detach(h1); 
  15.     } 
  16.   }; 
  17.  
  18. let count = 0; 
  19.  
  20. class App extends SvelteComponent { 
  21.   constructor(options) { 
  22.     super(); 
  23.     init(this, options, null, create_fragment, safe_not_equal, {}); 
  24.   } 
  25.  
  26. export default App; 

create_fragment

首先來看create_fragment方法,他是編譯器根據(jù)App的UI編譯而成,提供該組件與瀏覽器交互的方法,在上述編譯結(jié)果中,包含3個方法:

  • c,代表create,用于根據(jù)模版內(nèi)容,創(chuàng)建對應(yīng)DOM Element。例子中創(chuàng)建H1對應(yīng)DOM Element:
  1. h1 = element("h1"); 
  2. h1.textContent = `${count}`; 
  • m,代表mount,用于將c創(chuàng)建的DOM Element插入頁面,完成組件首次渲染。例子中會將H1插入頁面:
  1. insert(target, h1, anchor); 

insert方法會調(diào)用target.insertBefore:

  1. function insert(target, node, anchor) { 
  2.   target.insertBefore(node, anchor || null); 
  • d,代表detach,用于將組件對應(yīng)DOM Element從頁面中移除。例子中會移除H1:
  1. if (detaching) detach(h1); 

detach方法會調(diào)用parentNode.removeChild:

  1. function detach(node) { 
  2.   node.parentNode.removeChild(node); 

仔細觀察流程圖,會發(fā)現(xiàn)App組件編譯的產(chǎn)物沒有圖中fragment內(nèi)的p方法。

這是因為App沒有「變化狀態(tài)」的邏輯,所以相應(yīng)方法不會出現(xiàn)在編譯產(chǎn)物中。

可以發(fā)現(xiàn),create_fragment返回的c、m方法用于組件首次渲染。那么是誰調(diào)用這些方法呢?

SvelteComponent

每個組件對應(yīng)一個繼承自SvelteComponent的class,實例化時會調(diào)用init方法完成組件初始化,create_fragment會在init中調(diào)用:

  1. class App extends SvelteComponent { 
  2.   constructor(options) { 
  3.     super(); 
  4.     init(this, options, null, create_fragment, safe_not_equal, {}); 
  5.   } 

總結(jié)一下,流程圖中虛線部分在Demo1中的編譯結(jié)果為:

  • fragment:編譯為create_fragment方法的返回值
  • UI:create_fragment返回值中m方法的執(zhí)行結(jié)果
  • ctx:代表組件的上下文,由于例子中只包含一個不會改變的狀態(tài)count,所以ctx就是count的聲明語句

可以改變狀態(tài)的Demo

現(xiàn)在修改Demo,增加update方法,為H1綁定點擊事件,點擊后count改變:

  1. <h1 on:click="{update}">{count}</h1> 
  2.  
  3. <script> 
  4.   let count = 0; 
  5.   function update() { 
  6.     count++; 
  7.   } 
  8. </script> 

完整代碼見Demo2 repl[2]

編譯產(chǎn)物發(fā)生變化,ctx的變化如下:

  1. // 從module頂層的聲明語句 
  2. let count = 0; 
  3.  
  4. // 變?yōu)閕nstance方法 
  5. function instance($$self, $$props, $$invalidate) { 
  6.   let count = 0; 
  7.  
  8.   function update() { 
  9.     $$invalidate(0, count++, count); 
  10.   } 
  11.  
  12.   return [countupdate]; 

count從module頂層的聲明語句變?yōu)閕nstance方法內(nèi)的變量。之所以產(chǎn)生如此變化是因為App可以實例化多個:

  1. // 模版中定義3個App 
  2. <App/> 
  3. <App/> 
  4. <App/> 
  5.  
  6. // 當count不可變時,頁面渲染為:<h1>0</h1> 
  7. <h1>0</h1> 
  8. <h1>0</h1> 

當count不可變時,所有App可以復用同一個count。但是當count可變時,根據(jù)不同App被點擊次數(shù)不同,頁面可能渲染為:

  1. <h1>0</h1> 
  2. <h1>3</h1> 
  3. <h1>1</h1> 

所以每個App需要有獨立的上下文保存count,這就是instance方法的意義。推廣來說,Svelte編譯器會追蹤<script>內(nèi)所有變量聲明:

  • 是否包含改變該變量的語句,比如count++
  • 是否包含重新賦值的語句,比如count = 1
  • 等等情況

一旦發(fā)現(xiàn),就會將該變量提取到instance中,instance執(zhí)行后的返回值就是組件對應(yīng)ctx。

同時,如果執(zhí)行如上操作的語句可以通過模版被引用,則該語句會被$$invalidate包裹。

在Demo2中,update方法滿足:

  • 包含改變count的語句 —— count++
  • 可以通過模版被引用 —— 作為點擊回調(diào)函數(shù)

所以編譯后的update內(nèi)改變count的語句被$$invalidate方法包裹:

  1. // 源代碼中的update 
  2. function update() { 
  3.   count++; 
  4.  
  5. // 編譯后instance中的update 
  6. function update() { 
  7.   $$invalidate(0, count++, count); 

從流程圖可知,$$invalidate方法會執(zhí)行如下操作:

  • 更新ctx中保存狀態(tài)的值,比如Demo2中count++
  • 標記dirty,即標記App UI中所有和count相關(guān)的部分將會發(fā)生變化
  • 調(diào)度更新,在microtask中調(diào)度本次更新,所有在同一個macrotask中執(zhí)行的$$invalidate都會在該macrotask執(zhí)行完成后被統(tǒng)一執(zhí)行,最終會執(zhí)行組件fragment中的p方法

p方法是Demo2中新的編譯產(chǎn)物,除了p之外,create_fragment已有的方法也產(chǎn)生相應(yīng)變化:

  1. c() { 
  2.   h1 = element("h1"); 
  3.   // count的值變?yōu)閺腸tx中獲取 
  4.   t = text(/*count*/ ctx[0]); 
  5. }, 
  6. m(target, anchor) { 
  7.   insert(target, h1, anchor); 
  8.   append(h1, t); 
  9.   // 事件綁定 
  10.   dispose = listen(h1, "click", /*update*/ ctx[1]); 
  11. }, 
  12. p(ctx, [dirty]) { 
  13.   // set_data會更新t保存的文本節(jié)點 
  14.   if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]); 
  15. }, 
  16. d(detaching) { 
  17.   if (detaching) detach(h1); 
  18.   // 事件解綁 
  19.   dispose(); 

p方法會執(zhí)行$$invalidate中標記為dirty的項對應(yīng)的更新函數(shù)。

在Demo2中,App UI中只引用了狀態(tài)count,所以update方法中只有一個if語句,如果UI中引用了多個狀態(tài),則p方法中也會包含多個if語句:

  1. // UI中引用多個狀態(tài)  
  2. <h1 on:click="{count0++}">{count0}</h1> 
  3. <h1 on:click="{count1++}">{count1}</h1> 
  4. <h1 on:click="{count2++}">{count2}</h1> 

 對應(yīng)p方法包含多個if語句:

  1. p(new_ctx, [dirty]) { 
  2.   ctx = new_ctx; 
  3.   if (dirty & /*count*/ 1) set_data(t0, /*count*/ ctx[0]); 
  4.   if (dirty & /*count1*/ 2) set_data(t2, /*count1*/ ctx[1]); 
  5.   if (dirty & /*count2*/ 4) set_data(t4, /*count2*/ ctx[2]); 
  6. }, 

Demo2完整的更新步驟如下:

  1. 點擊H1觸發(fā)回調(diào)函數(shù)update
  2. update內(nèi)調(diào)用$$invalidate,更新ctx中的count,標記count為dirty,調(diào)度更新
  3. 執(zhí)行p方法,進入dirty的項(即count)對應(yīng)if語句,執(zhí)行更新對應(yīng)DOM Element的方法

總結(jié)

Svelte的完整工作流程會復雜的多,但是核心實現(xiàn)便是如此。

我們可以直觀的感受到,借由模版語法的約束,經(jīng)過編譯優(yōu)化,可以直接建立「狀態(tài)與要改變的DOM節(jié)點的對應(yīng)關(guān)系」。

在Demo2中,狀態(tài)count的變化直接對應(yīng)p方法中一個if語句,使得Svelte執(zhí)行「細粒度的更新」時對比使用虛擬DOM的框架更有性能優(yōu)勢。

上述性能分析中第四行「select row」就是一個「細粒度的更新」。想比較之下,React(倒數(shù)第三列)性能就差很多。

參考資料

[1]Demo1 repl:

https://svelte.dev/repl/9945d189204a4168b4c23890f1d92a3a?version=3.19.1[2]Demo2 repl:

https://svelte.dev/repl/bf22a31a0eff4875b5b3084aa2b85fc3?version=3.19.1

 

責任編輯:姜華 來源: 魔術(shù)師卡頌
相關(guān)推薦

2021-10-31 23:57:33

Eslint原理

2023-06-02 16:28:01

2016-12-26 18:05:00

單點登錄原理簡單實現(xiàn)

2022-05-06 09:22:25

Go泛型

2022-03-16 22:24:50

ReactstateHooks

2022-03-11 19:54:07

Svelte應(yīng)用程序JavaScript

2023-06-13 18:24:26

TypeScriptJSDoc開發(fā)

2021-09-06 05:59:17

Svelte前端框架

2024-10-28 00:01:00

2009-11-06 09:22:46

WCF應(yīng)用

2010-06-21 10:42:50

BitTorrent協(xié)

2010-09-01 11:43:06

KickstartPXE無人值守

2024-12-23 15:05:29

2014-06-06 09:01:07

DHCP

2017-12-06 16:28:48

Synchronize實現(xiàn)原理

2023-06-24 22:14:23

2021-08-16 09:59:52

ReactSvelte開發(fā)

2010-08-31 19:53:25

DHCP功能

2012-05-10 13:42:26

Java網(wǎng)絡(luò)爬蟲

2011-06-19 11:48:27

百度蜘蛛
點贊
收藏

51CTO技術(shù)棧公眾號