Hello, everyone. I am Karsong.
Svelte has been around for a long time, and I wanted to write an easy to understand principle analysis article.
This article will be around a flow chart and two Demo explain, the correct way to eat is to use the computer to open this article, follow the flow chart, Demo while watching, while typing, while learning.
Let’s get started.
Welcome to join the Human high quality front-end framework research group, Band fly
Demo1
The realization principle of Svelte is shown as follows:
Component is the Component written by the developer. The dotted line inside is compiled by the Svelte compiler. The arrows in the figure are the workflow at run time.
At compile time, consider the following App component code:
<h1>{count}</h1>
<script>
let count = 0;
</script>
Copy the code
See Demo1 repL for the complete code
The browser will display:
This code is compiled by the compiler to produce the following code, consisting of three parts:
create_fragment
methodscount
Declaration statement ofclass App
Declaration statement of
// omit some code...
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(a); init(this, options, null, create_fragment, safe_not_equal, {}); }}export default App;
Copy the code
create_fragment
Create_fragment method create_fragment method create_fragment method create_fragment method create_fragment method create_fragment method create_fragment method create_fragment method create_fragment
c
On behalf ofcreate
Is used to create mappings based on the template contentDOM Element
. Create in the exampleH1
The correspondingDOM Element
:
h1 = element("h1");
h1.textContent = `${count}`;
Copy the code
m
On behalf ofmount
, are used toc
To create theDOM Element
Insert the page and finish rendering the component for the first time. In this example, it’s going to beH1
Insert page:
insert(target, h1, anchor);
Copy the code
The insert method calls target.insertbefore:
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
Copy the code
d
On behalf ofdetach
Is used to map componentsDOM Element
Removed from the page. In this example it will be removedH1
:
if (detaching) detach(h1);
Copy the code
The detach method calls parentNode.removeChild:
function detach(node) {
node.parentNode.removeChild(node);
}
Copy the code
If you look closely at the flow chart, you will find that the compiled product of the App component does not have the P method in the fragment shown in the figure.
This is because the App has no logic to change state, so the corresponding method does not appear in the compiled product.
As you can see, the c and M methods returned by create_fragment are used for the first rendering of the component. So who calls these methods?
SvelteComponent
Create_fragment (SvelteComponent); create_fragment (SvelteComponent); create_fragment (SvelteComponent);
class App extends SvelteComponent {
constructor(options) {
super(a); init(this, options, null, create_fragment, safe_not_equal, {}); }}Copy the code
To summarize, the dashed line part of the flowchart is compiled in Demo1 as follows:
-
Fragment: compiled as the return value of the create_fragment method
-
UI: execution result of m method in create_fragment return value
-
CTX: represents the context of the component, and since the example contains only one unchanging state count, CTX is the declaration of count
Demo that can change state
Now modify Demo and add the update method to H1 binding event and count changes after clicking:
<h1 on:click="{update}">{count}</h1>
<script>
let count = 0;
function update() {
count++;
}
</script>
Copy the code
See Demo2 repL for the complete code
The compilation product changes, and CTX changes as follows:
// Declarations from the top of module
let count = 0;
// Change to the instance method
function instance($$self, $$props, $$invalidate) {
let count = 0;
function update() {
$$invalidate(0, count++, count);
}
return [count, update];
}
Copy the code
Count changes from a declaration at the top of a Module to a variable in the instance method. The reason for this change is that the App can instantiate more than one:
// Define 3 apps in the template<App/>
<App/>
<App/>// When count is immutable, the page renders as:<h1>0</h1>
<h1>0</h1>
<h1>0</h1>
Copy the code
When count is immutable, all apps can reuse the same count. However, when count is variable, the page may render as:
<h1>0</h1>
<h1>3</h1>
<h1>1</h1>
Copy the code
So each App needs to have a separate context to hold count, and that’s what the instance method is all about. To generalize, the Svelte compiler keeps track of all variable declarations in
-
Contains statements that change the variable, such as count++
-
Whether to include a reassignment statement, such as count = 1
-
And so on situation
Once found, the variable is extracted into instance, and the return value of instance execution is the component’s CTX.
At the same time, if the statement that performs the above operation can be referenced by the template, it will be wrapped with $$invalidate.
In Demo2, the update method satisfies:
-
Contains the statement that changes count — count++
-
Can be referenced via templates — as clickback functions
So the compiled update statement that changes the count is wrapped in the $$invalidate method:
// Update in source code
function update() {
count++;
}
Update in instance after compilation
function update() {
$$invalidate(0, count++, count);
}
Copy the code
As can be seen from the flowchart, the $$invalidate method performs the following operations:
-
Update the value of the saved state in CTX, such as count++ in Demo2
-
Marking dirty, marking all parts of the App UI related to count, will now change
-
Schedule the update in microTask. All $$invalidate executed in the same MacroTask will be executed uniformly after the macroTask is executed, and finally p method in component fragment will be executed
The p method is a new build of Demo2, and the create_fragment method has changed as well:
c() {
h1 = element("h1");
// The value of count becomes fetched from CTX
t = text(/*count*/ ctx[0]);
},
m(target, anchor) {
insert(target, h1, anchor);
append(h1, t);
// Event binding
dispose = listen(h1, "click"./*update*/ ctx[1]);
},
p(ctx, [dirty]) {
// set_data updates the text node saved by t
if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
},
d(detaching) {
if (detaching) detach(h1);
// Unbind events
dispose();
}
Copy the code
The p method performs the update function corresponding to the items marked dirty in $$invalidate.
In Demo2, the App UI only refers to the state count, so there is only one if statement in the update method, and if the UI references multiple states, there will be multiple if statements in the p method:
// Multiple states are referenced in UI<h1 on:click="{count0++}">{count0}</h1>
<h1 on:click="{count1++}">{count1}</h1>
<h1 on:click="{count2++}">{count2}</h1>
Copy the code
The corresponding p method 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]);
},
Copy the code
The complete Demo2 update steps are as follows:
-
Click H1 to trigger the callback function update
-
Update CTX with $$invalidate ($$invalidate)
-
Execute the p method, enter the if statement for the dirty item (that is, count), and perform the method to update the corresponding DOM Element
conclusion
The full workflow of Svelte is much more complex, but the core implementation is.
We can intuitively feel that by virtue of the constraints of template syntax, the corresponding relationship between the state and the DOM node to be changed can be directly established after compilation optimization.
In Demo2, the change in the state count corresponds directly to an if statement in the P method, giving Svelte a performance advantage when performing fine-grained updates compared to frameworks using the virtual DOM.
The fourth select Row in the above performance analysis is a fine-grained update. React (the third-to-last column), by comparison, performs much worse.