JavaScript開發簡單易懂的Svelte實現原理詳解
目錄
- Demo1
- create_fragment
- SvelteComponent
- 可以改變狀態的Demo
Svelte問世很久了,一直想寫一篇好懂的原理分析文章,拖了這麼久終於寫了。
Demo1
首先來看編譯時,考慮如下App
元件程式碼:
<h1>{count}</h1> <script> let count = 0; </script>
這段程式碼經由編譯器編譯後產生如下程式碼,包括三部分:
create_fragment
方法
count
的宣告語句
class App
的宣告語句
// 省略部分程式碼… function create_fragment(ctx) { let h1; return { c() { h1 = element("h1"); h1.textContent = `${count}`; },m(target,anchor) { insert(target,h1,anchor); },d(detaching) { if (detaching) detach(h1); } }; } let count = 0; class App extends SvelteComponent { constructor(options) { super(); init(this,options,null,create_fragment,safe_not_equal,{}); } } export default App;
create_fragment
首先來看create_fragment
方法,他是編譯器根據App
的UI
編譯而成,提供該元件與瀏覽器互動的方法,在上述編譯結果中,包含3個方法:
c
,代表create
,用於根據模版內容,建立對應DOM Element
。例子中建立H1
對應DOM Element
:
h1 = element("h1"); h1.textContent = `${count}`;
m
,代表mount
,用於將c
建立的DOM Element
插入頁面,完成元件首次渲染。例子中會將H1
插入頁面:
insert(target,anchor);
insert
方法會呼叫target.insertBefore
function insert(target,node,anchor) { target.insertBefore(node,anchor || null); }
d
,代表detach
,用於將元件對應DOM Element
從頁面中移除。例子中會移除H1
:
if (detaching) detach(h1);
detach
方法會呼叫parentNode.removeChild
:
function detach(node) { node.parentNode.removeChild(node); }
仔細觀察流程圖,會發現App
元件編譯的產物沒有圖中fragment
內的p
這是因為App
沒http://www.cppcns.com有變化狀態的邏輯,所以相應方法不會出現在編譯產物中。
可以發現,create_fragment
返回的c
、m
方法用於元件首次渲染。那麼是誰呼叫這些方法呢?
SvelteComponent
每個元件對應一個繼承自SvelteComponent
的class
,例項化時會呼叫init
方法完成元件初始化,create_fragment
會在init
中呼叫:
class App extends SvelteComponent { constructor(options) { super(); init(this,{}); } }
總結一下,流程圖中虛線部分在Demo1
中的編譯結果為:
fragment
:編譯為create_fragment
方法的返回值
UI
:create_fragment
返回值中m
方法的執行結果
ctx
:代表元件的上下文,由於例子中只包含一個不會改變的狀態count
,所以ctx
就是count
的宣告語句
可以改變狀態的Demo
現在修改Demo
,增加update
方法,為H1
繫結點選事件,點選後count
改變:
<h1 on:click="{updatewww.cppcns.com}">{count}</h1> <script> let count = 0; function update() { count++; } </script>
編譯產物發生變化,ctx
的變化如下:
// 從module頂層的宣告語句 let count = 0; // 變為instance方法 function instance($$self,$$props,$$invalidate) { let count = 0; function update() { $$invalidate(0,count++,count); } return [count,update]; }
count
從module
頂層的宣告語句變為instance
方法內的變數。之所以產生如此變化是因為App
可以例項化多個:
// 模版中定義3個App <App/> <App/> <App/> // 當count不可變時,頁面渲染為:<h1>0</h1> <h1>0</h1> <h1>0</h1&gezgPDfNt;
當count
不可變時,所有App
可以複用同一個count
。但是當count
可變時,根據不同App
被點選次數不同,頁面可能渲染為:
<h1>0</h1> <h1>3</h1> <h1>1</h1>
所以每個App
需要有獨立的上下文儲存count
,這就是instance
方法的意義。推廣來說,Svelte
編譯器會追蹤<script>
內所有變數宣告:
- 是否包含改變該變數的語句,比如
count++
- 是否包含重新賦值的語句,比如
count = 1
- 等等情況
一旦發現,就會將該變數提取到instance
中,instance
執行後的返回值就是元件對應ctx
。
同時,如果執行如上操作的語句可以通過模版被引用,則該語句會被$$invalidate
包裹。
在Demo2
中,update
方法滿足:
- 包含改變
count
的語句 ——count++
- 可以通過模版被引用 —— 作為點選回撥函式
所以編譯後的update
內改變count
的語句被$$invalidate
方法包裹:
// 原始碼中的update function update() { count++; } // 編譯後instance中的update function update() { $$invalidate(0,count); }
- 更新
ctx
中儲存狀態的值,比如Demo2
中count++
- 標記
dirty
,即標記App UI
中所有和count
相關的部分將會發生變化 - 排程更新,在
microtask
中排程本次更新,所有在同一個macrotask
中執行的$$invalidate
都會在該macrotask
執行完成後被統一執行,最終會執行元件fragment
中的p
方法
p
方法是Demo2
中新的編譯產物,除了p
之外,create_fragment
已有的方法也產生相應變化:
c() {
h1 = element("h1")http://www.cppcns.com;
// count的值變為從ctx中獲取
t = text(/*count*/ ctx[0]);
},anchor) {
insert(target,anchor);
append(h1,t);
// 事件繫結
dispose = listen(h1,"click",/*update*/ ctx[1]);
},p(ctx,[dirty]) {
// set_data會更新t儲存的文字節點
if (dirty & /*count*/ 1) set_data(t,/*count*/ ctx[0]);
},d(detaching) {
if (detaching) detach(h1);
// 事件解綁
dispose();
}
p
方法會執行$$invalidate
中標記為dirty
的項對應的更新函式。
在Demo2
中,App UI
中只引用了狀態count
,所以update
方法中只有一個if
語句,如果UI
中引用了多個狀態,則p
方法中也會包含多個if
語句:
// UI中引用多個狀態 <h1 on:click="{count0++}">{count0}</h1> <h1 on:click="{count1++}">{count1}</h1> <h1 on:click="{count2++}">{count2}</h1>
對應p
方法包含多個if
語句:
p(new_ctx,[dirty]) { ctx = new_ctx; if (dirty & /*count*/ 1) set_data(t0,/*count*/ ctx[0]); if (dirty & /*count1*/ 2) set_data(t2,/*count1*/ ctx[1]); if (dirty & /*count2*/ 4) set_data(t4,/*count2*/ ctx[2]); },
Demo2
完整的更新步驟如下:
- 點選
H1
觸發回撥函式update
update
內呼叫$$invalidate
,更新ctx
中的counhttp://www.cppcns.comt
,標記count
為dirty
,排程更新- 執行
p
方法,進入dirty
的項(即count
)對應if
語句,執行更新對應DOM Element
的方法
以上就是開發Svelte實現原理詳解的詳細內容,更多關於Svelte實現原理的資料請關注我們其它相關文章!