简单、好懂的Svelte实现原理
Demo1
<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
c,代表create,用于根据模版内容,创建对应DOM Element。例子中创建H1对应DOM Element:
h1 = element("h1");
h1.textContent = `${count}`;
m,代表mount,用于将c创建的DOM Element插入页面,完成组件首次渲染。例子中会将H1插入页面:
insert(target, h1, 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);
}
SvelteComponent
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
fragment:编译为create_fragment方法的返回值
UI:create_fragment返回值中m方法的执行结果
ctx:代表组件的上下文,由于例子中只包含一个不会改变的状态count,所以ctx就是count的声明语句
可以改变状态的Demo
<h1 on:click="{update}">{count}</h1>
<script>
let count = 0;
function update() {
count++;
}
</script>
// 从module顶层的声明语句
let count = 0;
// 变为instance方法
function instance($$self, $$props, $$invalidate) {
let count = 0;
function update() {
$$invalidate(0, count++, count);
}
return [count, update];
}
// 模版中定义3个App
<App/>
<App/>
<App/>
// 当count不可变时,页面渲染为:<h1>0</h1>
<h1>0</h1>
<h1>0</h1>
<h1>0</h1>
<h1>3</h1>
<h1>1</h1>
是否包含改变该变量的语句,比如count++ 是否包含重新赋值的语句,比如count = 1 等等情况
包含改变count的语句 —— count++ 可以通过模版被引用 —— 作为点击回调函数
// 源代码中的update
function update() {
count++;
}
// 编译后instance中的update
function update() {
$$invalidate(0, count++, count);
}
更新ctx中保存状态的值,比如Demo2中count++
标记dirty,即标记App UI中所有和count相关的部分将会发生变化
调度更新,在microtask中调度本次更新,所有在同一个macrotask中执行的$$invalidate都会在该macrotask执行完成后被统一执行,最终会执行组件fragment中的p方法
c() {
h1 = element("h1");
// count的值变为从ctx中获取
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会更新t保存的文本节点
if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
},
d(detaching) {
if (detaching) detach(h1);
// 事件解绑
dispose();
}
// UI中引用多个状态
<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]);
},
点击H1触发回调函数update
update内调用$$invalidate,更新ctx中的count,标记count为dirty,调度更新
执行p方法,进入dirty的项(即count)对应if语句,执行更新对应DOM Element的方法
总结
评论