If you have any questions or feedback, pleasefill out this form
This post is translated by ChatGPT and originally written in Mandarin, so there may be some inaccuracies or mistakes.
Introduction
From the core principles of Svelte, it's clear that Svelte aims to extract necessary information during the compilation process to minimize dynamic overhead. The previous article explained how Svelte operates from compilation to code generation. Today, we'll take a closer look at how the generated code from Svelte works.
Let's first examine a simple Svelte component:
<script>
import { onMount } from 'svelte';
let count = 1;
onMount(() => {
setInterval(() => count++, 1000);
})
</script>
{#if count != 100}
<span>{count}</span>
{/if}
<p>
this is text
</p>
The syntax for Svelte components is similar to standard HTML, with the addition of template-like syntax (such as if and await), making it largely compatible with HTML. However, the generated component is in JavaScript
. For example, the above component compiles to:
// Generated by Svelte, with some code omitted
import { onMount } from "svelte";
function create_if_block(ctx) {
let span;
let t;
return {
c() {
span = element("span");
t = text(/*count*/ ctx[0]);
},
m(target, anchor) {
insert(target, span, anchor);
append(span, t);
},
p(ctx, dirty) {
if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
},
d(detaching) {
if (detaching) detach(span);
}
};
}
function create_fragment(ctx) {
let t0;
let p;
let if_block = /*count*/ ctx[0] != 100 && create_if_block(ctx);
return {
c() {
if (if_block) if_block.c();
t0 = space();
p = element("p");
p.textContent = "this is text";
},
m(target, anchor) {
if (if_block) if_block.m(target, anchor);
insert(target, t0, anchor);
insert(target, p, anchor);
},
p(ctx, [dirty]) {
if (/*count*/ ctx[0] != 100) {
if (if_block) {
if_block.p(ctx, dirty);
} else {
if_block = create_if_block(ctx);
if_block.c();
if_block.m(t0.parentNode, t0);
}
} else if (if_block) {
if_block.d(1);
if_block = null;
}
},
i: noop,
o: noop,
d(detaching) {
if (if_block) if_block.d(detaching);
if (detaching) detach(t0);
if (detaching) detach(p);
}
};
}
function instance($$self, $$props, $$invalidate) {
let count = 1;
onMount(() => {
setInterval(() => $$invalidate(0, count++, count), 1000);
});
return [count];
}
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}
Svelte also supports SSR (Server-Side Rendering) functionality. If we compile the above code using Svelte's SSR feature, it will create a function that generates an HTML string.
// Generated by Svelte, with some code omitted
import { onMount } from "svelte";
const App = create_ssr_component(($$result, $$props, $$bindings, slots) => {
let count = 1;
onMount(() => {
setInterval(() => count++, 1000);
});
return `${count != 100 ? `<span>${escape(count)}</span>` : ``}
<p>this is text
</p>`;
});
export default App;
Observing Generated Code (DOM)
For the sake of clarity, we will focus on the generated code related to DOM
, skipping the SSR part for now.
The generated code primarily consists of three parts: the create_fragment
function, the instance
function, and the SvelteComponent
class.
create_fragment
Let's first take a look at create_fragment
:
function create_fragment(ctx) {
let t0;
let p;
let if_block = /*count*/ ctx[0] != 100 && create_if_block(ctx);
return {
c() {
if (if_block) if_block.c();
t0 = space();
p = element("p");
p.textContent = "this is text";
},
m(target, anchor) {
if (if_block) if_block.m(target, anchor);
insert(target, t0, anchor);
insert(target, p, anchor);
},
p(ctx, [dirty]) {
if (/*count*/ ctx[0] != 100) {
if (if_block) {
if_block.p(ctx, dirty);
} else {
if_block = create_if_block(ctx);
if_block.c();
if_block.m(t0.parentNode, t0);
}
} else if (if_block) {
if_block.d(1);
if_block = null;
}
},
i: noop,
o: noop,
d(detaching) {
if (if_block) if_block.d(detaching);
if (detaching) detach(t0);
if (detaching) detach(p);
}
};
}
The create_fragment
function returns an object containing several properties represented by single letters, which may seem ambiguous at first glance. However, these represent actions to be performed at different lifecycle stages:
- c: Stands for
create
, the function to execute when the component is created. - m: Stands for
mount
, the function to execute when the component is mounted to the DOM. - p: Stands for
patch
, the function to execute when the component is updated. - i: Stands for
intro
, the function to execute during the component's transition into the DOM. - o: Stands for
outro
, the function to execute during the component's transition out of the DOM. - d: Stands for
destroy
ordetach
, the function to execute when the component is unmounted.
You can refer to the detailed source code and generation logic at src/compiler/compile/render_dom/Block.ts.
Understanding what each letter represents makes it much clearer what the code is doing:
- It assigns the result of the condition (
if count != 100
) toif_block
. - During
create
:- It creates the
p
element. - It sets
p.textContent
tothis is text
.
- It creates the
- During
mount
:- If
if_block
istrue
, it callsif_block.m()
(the mount action). - It inserts
t0
into the anchor. - It inserts
p
into the anchor.
- If
- During
patch
:- If the condition
count != 100
is true:- If
if_block
exists, it callsif_block.p()
. - If it doesn't exist, it calls
create_if_block
and executesif_block.m()
.
- If
- If the condition
count != 100
is false:- This indicates that the contents of
if_block
should be removed, so it callsif_block.d()
.
- This indicates that the contents of
- If the condition
instance
Next, let's look at the instance
function:
function instance($$self, $$props, $$invalidate) {
let count = 1;
onMount(() => {
setInterval(() => $$invalidate(0, count++, count), 1000);
});
return [count];
}
The code inside the <script>
tag is encapsulated within the instance
function. There are a few notable points here:
- The original code
setInterval(() => count++, 1000)
is transformed intosetInterval(() => $$invalidate(0, count++, count), 1000)
after compilation. - The return value is an array that returns the value of
count
.
Svelte performs static analysis to gather information about variables, enabling it to handle dependency tracking. Here, $$invalidate
functions similarly to setState
in React. The key difference is that one requires manual invocation while Svelte automates detection and handling.
The implementation of $$invalidate
looks like this (with some code omitted):
// Marks the component as dirty (indicating it needs to be updated) if it detects a change in the variable value
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i]) $$.bound[i](value);
if (ready) make_dirty(component, i);
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
Every time setInterval
triggers count++
, $$invalidate
is called. It first compares the previous and current values. If there's an update, it invokes the mark_dirty
function and adds the component to dirty_components
, scheduling an update. Svelte also implements a batch update mechanism that aims to perform as many updates as possible in a single frame.
SvelteComponent
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, {});
}
}
The SvelteComponent
implementation is quite straightforward. It calls the init
function, which is primarily responsible for initializing the Svelte component and invoking create_fragment
and instance
, allowing the component to be mounted to the DOM.
Summary
The code generated by Svelte generally comprises three main components: create_fragment
, instance
, and SvelteComponent
.
- create_fragment: Instructs Svelte on how to handle each lifecycle phase of the component.
- instance: Executes the code within the
<script>
tag and returns the context (props, variables, etc.). - SvelteComponent: Initializes the Svelte component through the
init
function.
This article has elucidated how to interpret the code generated by Svelte and provided a basic overview of the underlying reactive mechanisms at play (although many mechanisms are not covered here and will be discussed in future articles). With this understanding, readers should now be able to comprehend the code produced by Svelte!
For more articles related to Svelte, you can refer to this link.
If you found this article helpful, please consider buying me a coffee ☕ It'll make my ordinary day shine ✨
☕Buy me a coffee