Table of Contents
Simulating the DOM tree with JS objects
Map from Virtual DOM to real DOM
Compare the differences between two virtual DOM trees
添加新节点
移除老节点
节点的替换
比较子节点
完整的代码
总结
Home Web Front-end JS Tutorial How to write your own virtual DOM? Method introduction

How to write your own virtual DOM? Method introduction

Oct 29, 2020 pm 05:30 PM
javascript node.js front end

How to write your own virtual DOM? Method introduction

To build your own virtual DOM, you need to know two things. You don't even need to dig into the source code of React or any other virtual DOM implementation because they are so large and complex - but in fact, the main part of the virtual DOM only takes less than 50 lines of code.

There are two concepts:

  • Virtual DOM is a mapping of the real DOM
  • When some nodes in the virtual DOM tree change, you will get A new virtual tree. The algorithm compares the two trees (new and old), finds the differences, and then just makes the corresponding changes on the real DOM.

Simulating the DOM tree with JS objects

First, we need to store the DOM tree in memory somehow. This can be done using normal JS objects. Suppose we have a tree like this:

Copy after login
Copy after login
      
  • item 1
  •   
  • item 2

Looks simple, right? How to represent it with a JS object?

{ type: ‘ul’, props: { ‘class’: ‘list’ }, children: [
  { type: ‘li’, props: {}, children: [‘item 1’] },
  { type: ‘li’, props: {}, children: [‘item 2’] }
] }
Copy after login

There are two things to note here:

  • Use the following objects to represent DOM elements
{ type: ‘…’, props: { … }, children: [ … ] }
Copy after login
Copy after login
  • Use ordinary JS strings to represent DOM text nodes

But there is a lot of content expressed in this way Dom trees are quite difficult. Let’s write an auxiliary function here to make it easier to understand:

function h(type, props, …children) {
  return { type, props, children };
}
Copy after login

Use this method to rearrange the initial code:

h(‘ul’, { ‘class’: ‘list’ },
  h(‘li’, {}, ‘item 1’),
  h(‘li’, {}, ‘item 2’),
);
Copy after login

This looks much simpler, and you can go further. JSX is used here, as follows:

Copy after login
Copy after login
      
  • item 1
  •   
  • item 2

is compiled into:

React.createElement(‘ul’, { className: ‘list’ },
  React.createElement(‘li’, {}, ‘item 1’),
  React.createElement(‘li’, {}, ‘item 2’),
);
Copy after login

Does it look familiar? If we can replace React.createElement(…) with the h(...) function we just defined, then we can also use JSX syntax. In fact, you only need to add this comment to the head of the source file:

/** @jsx h */
Copy after login
      
  • item 1
  •   
  • item 2

It actually tells Babel 'Hey, little brother, help me compile the JSX syntax, using h( ...) function instead of React.createElement(…), and then Babel starts compiling. '

To sum up, we write the DOM like this:

/** @jsx h */
const a = (
  
Copy after login
        
  • item 1
  •     
  • item 2
  •   
);

Babel will help us compile it into code like this:

const a = (
  h(‘ul’, { className: ‘list’ },
    h(‘li’, {}, ‘item 1’),
    h(‘li’, {}, ‘item 2’),
  );
);
Copy after login

When the function"h" When executed, it will return a normal JS object - that is, our virtual DOM:

const a = (
  { type: ‘ul’, props: { className: ‘list’ }, children: [
    { type: ‘li’, props: {}, children: [‘item 1’] },
    { type: ‘li’, props: {}, children: [‘item 2’] }
  ] }
);
Copy after login

Map from Virtual DOM to real DOM

Okay, now we have the DOM tree, use Normal JS object representation, as well as our own structures. This is cool, but we need to create a real DOM from it.

First let's make some assumptions and declare some terms:

  • Use variables starting with ' $ ' to represent real DOM nodes (elements, text nodes ), so $parent will be a real DOM element
  • The virtual DOM is represented using a variable named node

* just like in React , there can only be one root node - all other nodes are inside it

So, let's write a functioncreateElement(...), which will get a virtual DOM node and return a real DOM node. Ignore the props and children attributes here:

function createElement(node) {
  if (typeof node === ‘string’) {
    return document.createTextNode(node);
  }
  return document.createElement(node.type);
}
Copy after login

With the above method, I can also create two types of nodes, namely text nodes and Dom element nodes, which are types For the JS object:

{ type: ‘…’, props: { … }, children: [ … ] }
Copy after login
Copy after login

Therefore, you can pass in the virtual text node and virtual element node in the function createElement - this is feasible.

Now let's consider child nodes - each of them is a text node or element. So they can also be created with the createElement(…) function. Yes, this works like recursion, so we can call createElement(…) for each element's children and then use appendChild() to add to our element:

function createElement(node) {
  if (typeof node === ‘string’) {
    return document.createTextNode(node);
  }
  const $el = document.createElement(node.type);
  node.children
    .map(createElement)
    .forEach($el.appendChild.bind($el));
  return $el;
}
Copy after login

Wow, looks good. Put the node props properties aside first. Talk to you later. We don't need them to understand the basic concepts of virtual DOM as they add complexity.

The complete code is as follows:

/** @jsx h */

function h(type, props, ...children) {
  return { type, props, children };
}

function createElement(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  }
  const $el = document.createElement(node.type);
  node.children
    .map(createElement)
    .forEach($el.appendChild.bind($el));
  return $el;
}

const a = (
  
Copy after login
        
  • item 1
  •     
  • item 2
  •   
); const $root = document.getElementById('root'); $root.appendChild(createElement(a));

Compare the differences between two virtual DOM trees

Now we can convert the virtual DOM into a real DOM, which requires comparing the two trees DOM tree differences. Basically, we need an algorithm to compare the new tree with the old tree, which allows us to know what has changed, and then change the real DOM accordingly.

How to compare DOM trees? The following situations need to be handled:

  • To add a new node, use the appendChild(...) method to add a node

How to write your own virtual DOM? Method introduction

  • Remove old nodes, use removeChild(...) method to remove old nodes

How to write your own virtual DOM? Method introduction

  • Replacement of nodes , use the replaceChild(...) method

How to write your own virtual DOM? Method introduction

If the nodes are the same - you need to compare the child nodes in depth

How to write your own virtual DOM? Method introduction

编写一个名为 updateElement(…) 的函数,它接受三个参数—— $parentnewNodeoldNode,其中 $parent 是虚拟节点的一个实际 DOM 元素的父元素。现在来看看如何处理上面描述的所有情况。

添加新节点

function updateElement($parent, newNode, oldNode) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  }
}
Copy after login

移除老节点

这里遇到了一个问题——如果在新虚拟树的当前位置没有节点——我们应该从实际的 DOM 中删除它—— 这要如何做呢?

如果我们已知父元素(通过参数传递),我们就能调用 $parent.removeChild(…) 方法把变化映射到真实的 DOM 上。但前提是我们得知道我们的节点在父元素上的索引,我们才能通过 $parent.childNodes[index] 得到该节点的引用。

好的,让我们假设这个索引将被传递给 updateElement 函数(它确实会被传递——稍后将看到)。代码如下:

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  }
}
Copy after login

节点的替换

首先,需要编写一个函数来比较两个节点(旧节点和新节点),并告诉节点是否真的发生了变化。还有需要考虑这个节点可以是元素或是文本节点:

function changed(node1, node2) {
  return typeof node1 !== typeof node2 ||
         typeof node1 === ‘string’ && node1 !== node2 ||
         node1.type !== node2.type
}
Copy after login

现在,当前的节点有了 index 属性,就可以很简单的用新节点替换它:

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(
      createElement(newNode),
      $parent.childNodes[index]
    );
  }
}
Copy after login

比较子节点

最后,但并非最不重要的是——我们应该遍历这两个节点的每一个子节点并比较它们——实际上为每个节点调用updateElement(…)方法,同样需要用到递归。

  • 当节点是 DOM 元素时我们才需要比较( 文本节点没有子节点 )
  • 我们需要传递当前的节点的引用作为父节点
  • 我们应该一个一个的比较所有的子节点,即使它是 undefined 也没有关系,我们的函数也会正确处理它。
  • 最后是 index,它是子数组中子节点的 index
function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(
      createElement(newNode),
      $parent.childNodes[index]
    );
  } else if (newNode.type) {
    const newLength = newNode.children.length;
    const oldLength = oldNode.children.length;
    for (let i = 0; i <h2 id="完整的代码">完整的代码</h2><p><strong>Babel+JSX</strong><br>/<em>* @jsx h </em>/</p><pre class="brush:php;toolbar:false">function h(type, props, ...children) {
  return { type, props, children };
}

function createElement(node) {
  if (typeof node === 'string') {
    return document.createTextNode(node);
  }
  const $el = document.createElement(node.type);
  node.children
    .map(createElement)
    .forEach($el.appendChild.bind($el));
  return $el;
}

function changed(node1, node2) {
  return typeof node1 !== typeof node2 ||
         typeof node1 === 'string' && node1 !== node2 ||
         node1.type !== node2.type
}

function updateElement($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(
      createElement(newNode)
    );
  } else if (!newNode) {
    $parent.removeChild(
      $parent.childNodes[index]
    );
  } else if (changed(newNode, oldNode)) {
    $parent.replaceChild(
      createElement(newNode),
      $parent.childNodes[index]
    );
  } else if (newNode.type) {
    const newLength = newNode.children.length;
    const oldLength = oldNode.children.length;
    for (let i = 0; i 
    
Copy after login
  • item 1
  •     
  • item 2
  •    ); const b = (   
          
    • item 1
    •     
    • hello!
    •   
    ); const $root = document.getElementById('root'); const $reload = document.getElementById('reload'); updateElement($root, a); $reload.addEventListener('click', () => {   updateElement($root, b, a); });

    HTML

    <button>RELOAD</button>
    <p></p>
    Copy after login

    CSS

    #root {
      border: 1px solid black;
      padding: 10px;
      margin: 30px 0 0 0;
    }
    Copy after login

    打开开发者工具,并观察当按下“Reload”按钮时应用的更改。

    How to write your own virtual DOM? Method introduction

    总结

    现在我们已经编写了虚拟 DOM 实现及了解它的工作原理。作者希望,在阅读了本文之后,对理解虚拟 DOM 如何工作的基本概念以及在幕后如何进行响应有一定的了解。

    然而,这里有一些东西没有突出显示(将在以后的文章中介绍它们):

    • 设置元素属性(props)并进行 diffing/updating
    • 处理事件——向元素中添加事件监听
    • 让虚拟 DOM 与组件一起工作,比如React
    • 获取对实际DOM节点的引用
    • 使用带有库的虚拟 DOM,这些库可以直接改变真实的 DOM,比如 jQuery 及其插件

    原文地址:https://medium.com/@deathmood/how-to-write-your-own-virtual-dom-ee74acc13060

    作者:deathmood

    为了保证的可读性,本文采用意译而非直译。

    更多编程相关知识,请访问:编程入门!!

    The above is the detailed content of How to write your own virtual DOM? Method introduction. For more information, please follow other related articles on the PHP Chinese website!

    Statement of this Website
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

    Hot AI Tools

    Undresser.AI Undress

    Undresser.AI Undress

    AI-powered app for creating realistic nude photos

    AI Clothes Remover

    AI Clothes Remover

    Online AI tool for removing clothes from photos.

    Undress AI Tool

    Undress AI Tool

    Undress images for free

    Clothoff.io

    Clothoff.io

    AI clothes remover

    AI Hentai Generator

    AI Hentai Generator

    Generate AI Hentai for free.

    Hot Article

    R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
    4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Best Graphic Settings
    4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. How to Fix Audio if You Can't Hear Anyone
    4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
    WWE 2K25: How To Unlock Everything In MyRise
    1 months ago By 尊渡假赌尊渡假赌尊渡假赌

    Hot Tools

    Notepad++7.3.1

    Notepad++7.3.1

    Easy-to-use and free code editor

    SublimeText3 Chinese version

    SublimeText3 Chinese version

    Chinese version, very easy to use

    Zend Studio 13.0.1

    Zend Studio 13.0.1

    Powerful PHP integrated development environment

    Dreamweaver CS6

    Dreamweaver CS6

    Visual web development tools

    SublimeText3 Mac version

    SublimeText3 Mac version

    God-level code editing software (SublimeText3)

    PHP and Vue: a perfect pairing of front-end development tools PHP and Vue: a perfect pairing of front-end development tools Mar 16, 2024 pm 12:09 PM

    PHP and Vue: a perfect pairing of front-end development tools. In today's era of rapid development of the Internet, front-end development has become increasingly important. As users have higher and higher requirements for the experience of websites and applications, front-end developers need to use more efficient and flexible tools to create responsive and interactive interfaces. As two important technologies in the field of front-end development, PHP and Vue.js can be regarded as perfect tools when paired together. This article will explore the combination of PHP and Vue, as well as detailed code examples to help readers better understand and apply these two

    Questions frequently asked by front-end interviewers Questions frequently asked by front-end interviewers Mar 19, 2024 pm 02:24 PM

    In front-end development interviews, common questions cover a wide range of topics, including HTML/CSS basics, JavaScript basics, frameworks and libraries, project experience, algorithms and data structures, performance optimization, cross-domain requests, front-end engineering, design patterns, and new technologies and trends. . Interviewer questions are designed to assess the candidate's technical skills, project experience, and understanding of industry trends. Therefore, candidates should be fully prepared in these areas to demonstrate their abilities and expertise.

    Simple JavaScript Tutorial: How to Get HTTP Status Code Simple JavaScript Tutorial: How to Get HTTP Status Code Jan 05, 2024 pm 06:08 PM

    JavaScript tutorial: How to get HTTP status code, specific code examples are required. Preface: In web development, data interaction with the server is often involved. When communicating with the server, we often need to obtain the returned HTTP status code to determine whether the operation is successful, and perform corresponding processing based on different status codes. This article will teach you how to use JavaScript to obtain HTTP status codes and provide some practical code examples. Using XMLHttpRequest

    Is Django front-end or back-end? check it out! Is Django front-end or back-end? check it out! Jan 19, 2024 am 08:37 AM

    Django is a web application framework written in Python that emphasizes rapid development and clean methods. Although Django is a web framework, to answer the question whether Django is a front-end or a back-end, you need to have a deep understanding of the concepts of front-end and back-end. The front end refers to the interface that users directly interact with, and the back end refers to server-side programs. They interact with data through the HTTP protocol. When the front-end and back-end are separated, the front-end and back-end programs can be developed independently to implement business logic and interactive effects respectively, and data exchange.

    Exploring Go language front-end technology: a new vision for front-end development Exploring Go language front-end technology: a new vision for front-end development Mar 28, 2024 pm 01:06 PM

    As a fast and efficient programming language, Go language is widely popular in the field of back-end development. However, few people associate Go language with front-end development. In fact, using Go language for front-end development can not only improve efficiency, but also bring new horizons to developers. This article will explore the possibility of using the Go language for front-end development and provide specific code examples to help readers better understand this area. In traditional front-end development, JavaScript, HTML, and CSS are often used to build user interfaces

    Combination of Golang and front-end technology: explore how Golang plays a role in the front-end field Combination of Golang and front-end technology: explore how Golang plays a role in the front-end field Mar 19, 2024 pm 06:15 PM

    Combination of Golang and front-end technology: To explore how Golang plays a role in the front-end field, specific code examples are needed. With the rapid development of the Internet and mobile applications, front-end technology has become increasingly important. In this field, Golang, as a powerful back-end programming language, can also play an important role. This article will explore how Golang is combined with front-end technology and demonstrate its potential in the front-end field through specific code examples. The role of Golang in the front-end field is as an efficient, concise and easy-to-learn

    Django: A magical framework that can handle both front-end and back-end development! Django: A magical framework that can handle both front-end and back-end development! Jan 19, 2024 am 08:52 AM

    Django: A magical framework that can handle both front-end and back-end development! Django is an efficient and scalable web application framework. It is able to support multiple web development models, including MVC and MTV, and can easily develop high-quality web applications. Django not only supports back-end development, but can also quickly build front-end interfaces and achieve flexible view display through template language. Django combines front-end development and back-end development into a seamless integration, so developers don’t have to specialize in learning

    How to get HTTP status code in JavaScript the easy way How to get HTTP status code in JavaScript the easy way Jan 05, 2024 pm 01:37 PM

    Introduction to the method of obtaining HTTP status code in JavaScript: In front-end development, we often need to deal with the interaction with the back-end interface, and HTTP status code is a very important part of it. Understanding and obtaining HTTP status codes helps us better handle the data returned by the interface. This article will introduce how to use JavaScript to obtain HTTP status codes and provide specific code examples. 1. What is HTTP status code? HTTP status code means that when the browser initiates a request to the server, the service

    See all articles