Hi everyone, I'm Kasong.
0618cbf8135037 has been out for a long time, and I have always wanted to write an Svelte
This article will focus on a flow chart and two Demo
explanations. The correct way to eat is to open this article with a computer, follow the flow chart, Demo
while watching, typing, and learning.
Let me start.
Demo1
The implementation principle of Svelte
In the picture, Component
is a component written by the developer, and the internal dashed part is Svelte
compiler. The arrows in the figure are the workflow at runtime.
First look at the compilation, consider the following App
component code:
<h1>{count}</h1>
<script>
let count = 0;
</script>
For the complete code, see Demo1 repl
The browser will display:
This code is compiled by the compiler to produce the following code, including three parts:
create_fragment
Methodcount
statementclass App
statement
// 省略部分代码…
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
First look at the create_fragment
method, which is compiled by the compiler based on App
of UI
, which provides a method for the component to interact with the browser. In the above compilation result, there are 3 methods:
c
, on behalf ofcreate
, used to create the correspondingDOM Element
according to the template content. In the example, createH1
corresponding toDOM Element
:
h1 = element("h1");
h1.textContent = `${count}`;
m
, representingmount
, used to insertc
created byDOM Element
into the page to complete the first rendering of the component. In the example,H1
will be inserted into the page:
insert(target, h1, anchor);
insert
method will call target.insertBefore
:
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
d
, which stands fordetach
, is used to remove the component corresponding toDOM Element
from the page. In the example,H1
will be removed:
if (detaching) detach(h1);
detach
method will call parentNode.removeChild
:
function detach(node) {
node.parentNode.removeChild(node);
}
If you carefully observe the flowchart, you will find that the product compiled by the App
p
method fragment
in the figure.
This is because App
does not have change state , so the corresponding method will not appear in the compiled product.
Can be found, create_fragment
returned c
, m
method for rendering components for the first time. So who is calling these methods?
SvelteComponent
Each component corresponds to a SvelteComponent
inherited from 0618cbf813575a. class
init
method will be called to complete the component initialization, and create_fragment
will be called init
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, null, create_fragment, safe_not_equal, {});
}
}
To sum up, the result of the compilation of the dotted line in the flow chart in Demo1
fragment
: compiled to the return valuecreate_fragment
UI
:create_fragment
return valuem
execution result of the methodctx
: Represents the context of the component. Since the example only contains a state that will not changecount
,ctx
is the statement statement forcount
Demo that can change status
Now modify Demo
, add update
method, bind the click event H1
count
change:
<h1 on:click="{update}">{count}</h1>
<script>
let count = 0;
function update() {
count++;
}
</script>
For the complete code, see Demo2 repl
The compilation product changes, ctx
changes as follows:
// 从module顶层的声明语句
let count = 0;
// 变为instance方法
function instance($$self, $$props, $$invalidate) {
let count = 0;
function update() {
$$invalidate(0, count++, count);
}
return [count, update];
}
count
from module
declaration top becomes instance
variables within the method. The reason for this change is that App
can instantiate multiple:
// 模版中定义3个App
<App/>
<App/>
<App/>
// 当count不可变时,页面渲染为:<h1>0</h1>
<h1>0</h1>
<h1>0</h1>
When count
is immutable, all App
can reuse the same count
. But when count
variable, depending App
, the page may render as:
<h1>0</h1>
<h1>3</h1>
<h1>1</h1>
So each App
needs to have an independent context save count
, which is the meaning of the instance
In general, the Svelte
compiler will track all variable declarations in <script>
- Whether to include the statement to change the variable, such as
count++
- Whether to include reassignment statements, such as
count = 1
- Wait for the situation
Once found, the variable will be extracted to instance
, the return value after the execution of instance
ctx
.
At the same time, if the statement that performs the above operations can be referenced through the template, the statement will be wrapped $$invalidate
In Demo2
, the update
method satisfies:
- It includes changing
count
statement -count++
- Can be referenced through the template-as a click callback function
So compiled update
change within count
statements are $$invalidate
parcel method:
// 源代码中的update
function update() {
count++;
}
// 编译后instance中的update
function update() {
$$invalidate(0, count++, count);
}
As can be seen from the flowchart, the $$invalidate
method will perform the following operations:
- Update the
ctx
of the saved state incount++
Demo2
- Mark
dirty
, that is, allcount
related toApp UI
- Schedule update,
microtask
macrotask
executed in the same$$invalidate
will be executed uniformly after the execution ofmacrotask
p
method in thefragment
p
method Demo2
the new compiler product, in addition p
addition, create_fragment
conventional process also produces a corresponding change in:
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();
}
p
method executes $$invalidate
labeled dirty
corresponding to the entry of the update function.
In Demo2
in, App UI
only cited state count
, so update
method is only one if
statement, if UI
referenced in multiple states, the p
method will also contain multiple if
statement:
// UI中引用多个状态
<h1 on:click="{count0++}">{count0}</h1>
<h1 on:click="{count1++}">{count1}</h1>
<h1 on:click="{count2++}">{count2}</h1>
The method corresponding to p
contains multiple if
statements:
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
complete update steps for 0618cbf8135f8a are as follows:
- Click
H1
trigger the callback functionupdate
update
calls within$$invalidate
, updatectx
incount
, markcount
asdirty
, schedule update- Execute the
p
method, enter the item ofdirty
count
) corresponds to theif
statement, execute the update methodDOM Element
Summarize
Svelte
will be much more complicated, but the core implementation is like this.
We can intuitively feel, by means of constraint template syntax, compiled optimized to establish a direct state to change the correspondence between the DOM node .
In Demo2
, the change of count
directly corresponds to a if
statement in the p
Svelte
perform the fine-grained update , which has more performance advantages than the virtual DOM framework.
Analysis of the above properties in the fourth row SELECT Row is a update granular . For comparison, the React
(the third last column) is much worse.
Welcome to join the human high-quality front-end framework group , take flight
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。