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

HTML Editing Basics (A Must-Read for Newbies)

Open DREAMWEAVER and create a new HTML. . Propert...

Centos7 installation of FFmpeg audio/video tool simple document

ffmpeg is a very powerful audio and video process...

HTML tag full name and function introduction

Alphabetical DTD: Indicates in which XHTML 1.0 DT...

Why MySQL does not recommend using subqueries and joins

To do a paginated query: 1. For MySQL, it is not ...

How to use nginx to block a specified interface (URL)

1. Introduction Sometimes, after the web platform...

JavaScript event delegation principle

Table of contents 1. What is event delegation? 2....

Some suggestions for Linux system optimization (kernel optimization)

Disable swap If the server is running a database ...

Markup Language - Image Replacement

Click here to return to the 123WORDPRESS.COM HTML ...

Vue integrates PDF.js to implement PDF preview and add watermark steps

Table of contents Achieve results Available plugi...

Real-time refresh of long connection on Vue+WebSocket page

Recently, the Vue project needs to refresh the da...

Pure js to achieve typewriter effect

This article example shares the specific code of ...

A complete guide to CSS style attributes css() and width() in jQuery

Table of contents 1. Basic use of css(): 1.1 Get ...

How React Hooks Work

Table of contents 1. React Hooks vs. Pure Functio...