簡(jiǎn)單、好懂的Svelte實(shí)現(xiàn)原理
Demo1

<h1>{count}</h1>
<script>
let count = 0;
</script>

create_fragment方法 count的聲明語(yǔ)句 class App的聲明語(yǔ)句
// 省略部分代碼…
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
c,代表create,用于根據(jù)模版內(nèi)容,創(chuàng)建對(duì)應(yīng)DOM Element。例子中創(chuàng)建H1對(duì)應(yīng)DOM Element:
h1 = element("h1");
h1.textContent = `${count}`;
m,代表mount,用于將c創(chuàng)建的DOM Element插入頁(yè)面,完成組件首次渲染。例子中會(huì)將H1插入頁(yè)面:
insert(target, h1, anchor);
insert方法會(huì)調(diào)用target.insertBefore:
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
d,代表detach,用于將組件對(duì)應(yīng)DOM Element從頁(yè)面中移除。例子中會(huì)移除H1:
if (detaching) detach(h1);
detach方法會(huì)調(diào)用parentNode.removeChild:
function detach(node) {
node.parentNode.removeChild(node);
}

SvelteComponent
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
fragment:編譯為create_fragment方法的返回值
UI:create_fragment返回值中m方法的執(zhí)行結(jié)果
ctx:代表組件的上下文,由于例子中只包含一個(gè)不會(huì)改變的狀態(tài)count,所以ctx就是count的聲明語(yǔ)句
可以改變狀態(tài)的Demo
<h1 on:click="{update}">{count}</h1>
<script>
let count = 0;
function update() {
count++;
}
</script>
// 從module頂層的聲明語(yǔ)句
let count = 0;
// 變?yōu)閕nstance方法
function instance($$self, $$props, $$invalidate) {
let count = 0;
function update() {
$$invalidate(0, count++, count);
}
return [count, update];
}
// 模版中定義3個(gè)App
<App/>
<App/>
<App/>
// 當(dāng)count不可變時(shí),頁(yè)面渲染為:<h1>0</h1>
<h1>0</h1>
<h1>0</h1>
<h1>0</h1>
<h1>3</h1>
<h1>1</h1>
是否包含改變?cè)撟兞康恼Z(yǔ)句,比如count++ 是否包含重新賦值的語(yǔ)句,比如count = 1 等等情況
包含改變count的語(yǔ)句 —— count++ 可以通過(guò)模版被引用 —— 作為點(diǎn)擊回調(diào)函數(shù)
// 源代碼中的update
function update() {
count++;
}
// 編譯后instance中的update
function update() {
$$invalidate(0, count++, count);
}
更新ctx中保存狀態(tài)的值,比如Demo2中count++
標(biāo)記dirty,即標(biāo)記App UI中所有和count相關(guān)的部分將會(huì)發(fā)生變化
調(diào)度更新,在microtask中調(diào)度本次更新,所有在同一個(gè)macrotask中執(zhí)行的$$invalidate都會(huì)在該macrotask執(zhí)行完成后被統(tǒng)一執(zhí)行,最終會(huì)執(zhí)行組件fragment中的p方法

c() {
h1 = element("h1");
// count的值變?yōu)閺腸tx中獲取
t = text(/*count*/ ctx[0]);
},
m(target, anchor) {
insert(target, h1, anchor);
append(h1, t);
// 事件綁定
dispose = listen(h1, "click", /*update*/ ctx[1]);
},
p(ctx, [dirty]) {
// set_data會(huì)更新t保存的文本節(jié)點(diǎn)
if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
},
d(detaching) {
if (detaching) detach(h1);
// 事件解綁
dispose();
}
// UI中引用多個(gè)狀態(tài)
<h1 on:click="{count0++}">{count0}</h1>
<h1 on:click="{count1++}">{count1}</h1>
<h1 on:click="{count2++}">{count2}</h1>
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]);
},
點(diǎn)擊H1觸發(fā)回調(diào)函數(shù)update
update內(nèi)調(diào)用$$invalidate,更新ctx中的count,標(biāo)記count為dirty,調(diào)度更新
執(zhí)行p方法,進(jìn)入dirty的項(xiàng)(即count)對(duì)應(yīng)if語(yǔ)句,執(zhí)行更新對(duì)應(yīng)DOM Element的方法
總結(jié)


