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
destroyordetach, 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
pelement. - It sets
p.textContenttothis is text.
- It creates the
- During
mount:- If
if_blockistrue, it callsif_block.m()(the mount action). - It inserts
t0into the anchor. - It inserts
pinto the anchor.
- If
- During
patch:- If the condition
count != 100is true:- If
if_blockexists, it callsif_block.p(). - If it doesn't exist, it calls
create_if_blockand executesif_block.m().
- If
- If the condition
count != 100is false:- This indicates that the contents of
if_blockshould 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
initfunction.
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