Detailed explanation of the Svelte implementation principle for simple and easy JavaScript development

Detailed explanation of the Svelte implementation principle for simple and easy JavaScript development

Svelte has been around for a long time, and I have always wanted to write an easy-to-understand principle analysis article. After putting it off for so long, I finally wrote it.

Demo1

First, let's look at the compilation process. Consider the following App component code:

<h1>{count}</h1>
<script>
  let count = 0;
</script>

After being compiled by the compiler, this code generates the following code, which consists of three parts:

create_fragment method

count statement

Declaration statement of class App

// 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();
    init(this, options, null, create_fragment, safe_not_equal, {});
  }
} 
export default App;

create_fragment

First, let's look at the create_fragment method, which is compiled by the compiler based on App UI and provides a method for the component to interact with the browser. The above compilation result contains 3 methods:

c stands for create , which is used to create the corresponding DOM Element according to the template content. In this example, we create DOM Element corresponding to H1 :

h1 = element("h1");
h1.textContent = `${count}`;

m stands for mount , which is used to insert DOM Element created by c into the page and complete the first rendering of the component. In this example, H1 will be inserted into the page:

insert(target, h1, anchor);

The insert method calls target.insertBefore :

function insert(target, node, anchor) {
  target.insertBefore(node, anchor || null);
}

d stands for detach , which is used to remove the component's corresponding DOM Element from the page. In this example, H1 will be removed:

if (detaching) detach(h1);

The detach method calls parentNode.removeChild :

function detach(node) {
  node.parentNode.removeChild(node);
}

If you look closely at the flowchart, you will find that the compiled product of App component does not have the p method in fragment in the figure.

This is because App has no logic for changing state, so the corresponding method will not appear in the compiled product.

It can be found that the c and m methods returned by create_fragment are used for the first rendering of the component. So who calls these methods?

SvelteComponent

Each component corresponds to a class that inherits from SvelteComponent . When instantiated, init method will be called to complete component initialization. create_fragment will be called in init :

class App extends SvelteComponent {
  constructor(options) {
    super();
    init(this, options, null, create_fragment, safe_not_equal, {});
  }
}

To summarize, the compilation result of the dotted part in the flowchart in Demo1 is:

fragment : compiled as the return value of create_fragment method

UI : The execution result of the m method in create_fragment return value

ctx : represents the context of the component. Since the example only contains one state count that does not change, ctx is the declaration statement of count

Demo that can change state

Now modify Demo , add the update method, bind the click event to H1 , and change count after clicking:

<h1 on:click="{update}">{count}</h1> 
<script>
  let count = 0;
  function update() {
    count++;
  }
</script>

The compiled product changes, and the changes of ctx are as follows:

// From the top-level declaration of the module let count = 0; 
// Become an instance method function instance($$self, $$props, $$invalidate) {
  let count = 0; 
  function update() {
    $$invalidate(0, count++, count);
  } 
  return [count, update];
}

count changes from a declaration statement at the top level of module to a variable within instance method. The reason for this change is that App can instantiate multiple:

// Define 3 Apps in the template
<App/>
<App/>
<App/>
// When count is immutable, the page is rendered as: <h1>0</h1>
<h1>0</h1>
<h1>0</h1>

When count is immutable, all App can reuse the same count . But when count is variable, depending on the number of times different App are clicked, the page may be rendered as:

<h1>0</h1>
<h1>3</h1>
<h1>1</h1>

So each App needs to have an independent context to save count , which is the meaning of instance method. In general, the Svelte compiler tracks all variable declarations within <script> :

  • Does it contain statements that change the variable, such as count++
  • Whether it contains reassignment statements, such as count = 1
  • Wait for the situation

Once found, the variable will be extracted to instance , and the return value after instance is executed is ctx corresponding to the component.

At the same time, if the statement that performs the above operation can be referenced through the template, the statement will be wrapped by $$invalidate .

In Demo2 , the update method satisfies:

  • Contains statements that change count - count++
  • Can be referenced through the template - as a click callback function

So the statement that changes count in the compiled update is wrapped by $$invalidate method:

// update in source code
function update() {
  count++;
} 
// Update in compiled instance
function update() {
  $$invalidate(0, count++, count);
}
  • Update the value of the saved state in ctx , such as count++ in Demo2
  • Mark dirty , that is, mark all count related parts in App UI that will change
  • Schedule the update. Schedule this update in microtask . All $$invalidate executed in the same macrotask will be executed uniformly after macrotask is completed, and finally p method in the component fragment will be executed

The p method is a new compilation product in Demo2 . In addition to p , the existing methods of create_fragment also undergo corresponding changes:

c() {
  h1 = element("h1");
  //The value of count is obtained 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 will update the text node stored in t if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
},
d(detaching) {
  if (detaching) detach(h1);
  // Event unbinding dispose();
}

The p method will execute the update function corresponding to the item marked as dirty in $$invalidate .

In Demo2 , only the state count is referenced in App UI , so there is only one if statement in the update method. If multiple states are referenced in UI , the p method will also contain multiple if statements:

// Reference multiple states in UI<h1 on:click="{count0++}">{count0}</h1>
<h1 on:click="{count1++}">{count1}</h1>
<h1 on:click="{count2++}">{count2}</h1>

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]);
},

The complete update steps Demo2 are as follows:

  1. Click H1 to trigger the callback function update
  2. Call $$invalidate in update , update count in ctx , mark count as dirty , and schedule update
  3. Execute the p method, enter the dirty item ( count ) corresponding to the if statement, and execute the method to update the corresponding DOM Element

The above is the detailed content of the detailed explanation of the implementation principles of Svelte in JavaScript development. For more information about the implementation principles of Svelte, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • How to implement Svelte's Defer Transition in Vue
  • Example of Vue transition to achieve like animation effect
  • Detailed explanation of CSS transition of Vue transition effect (combined with transition, animation, animate.css)
  • How to create reusable Transitions in Vue

<<:  A MySQL migration plan and practical record of pitfalls

>>:  HTML+CSS makes div tag add delete icon in the upper right corner sample code

Recommend

MySQL 5.7.17 winx64 installation and configuration method graphic tutorial

Windows installation mysql-5.7.17-winx64.zip meth...

How to maintain MySQL indexes and data tables

Table of contents Find and fix table conflicts Up...

A brief discussion on the differences between FTP, FTPS and SFTP

Table of contents Introduction to FTP, FTPS and S...

How to implement web page compression in Nginx optimization service

Configure web page compression to save resources ...

MySQL 8.0.12 installation steps and basic usage tutorial under Windows

This article shares the installation steps and us...

A time-consuming troubleshooting process record of a docker error

Table of contents origin Environmental Informatio...

Web design tips on form input boxes

1. Dashed box when cancel button is pressed <br...

Weird and interesting Docker commands you may not know

Intro Introduces and collects some simple and pra...

How to use domestic image warehouse for Docker

1. Problem description Due to some reasons, the d...

Typical cases of MySQL index failure

Table of contents Typical Cases Appendix: Common ...

Detailed explanation of HTML basics (Part 2)

1. List The list ul container is loaded with a fo...

Use of Linux ln command

1. Command Introduction The ln command is used to...