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:
- 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’] } ] }
There are two things to note here:
- Use the following objects to represent DOM elements
{ type: ‘…’, props: { … }, children: [ … ] }
- 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 }; }
Use this method to rearrange the initial code:
h(‘ul’, { ‘class’: ‘list’ }, h(‘li’, {}, ‘item 1’), h(‘li’, {}, ‘item 2’), );
This looks much simpler, and you can go further. JSX is used here, as follows:
- item 1
- item 2
is compiled into:
React.createElement(‘ul’, { className: ‘list’ }, React.createElement(‘li’, {}, ‘item 1’), React.createElement(‘li’, {}, ‘item 2’), );
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 */
- 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 = (
- 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’), ); );
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’] } ] } );
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); }
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: [ … ] }
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; }
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 = (
- item 1
- item 2
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
- Remove old nodes, use removeChild(...) method to remove old nodes
- Replacement of nodes , use the replaceChild(...) method
If the nodes are the same - you need to compare the child nodes in depth
编写一个名为 updateElement(…) 的函数,它接受三个参数—— $parent
、newNode 和 oldNode,其中 $parent 是虚拟节点的一个实际 DOM 元素的父元素。现在来看看如何处理上面描述的所有情况。
添加新节点
function updateElement($parent, newNode, oldNode) { if (!oldNode) { $parent.appendChild( createElement(newNode) ); } }
移除老节点
这里遇到了一个问题——如果在新虚拟树的当前位置没有节点——我们应该从实际的 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] ); } }
节点的替换
首先,需要编写一个函数来比较两个节点(旧节点和新节点),并告诉节点是否真的发生了变化。还有需要考虑这个节点可以是元素或是文本节点:
function changed(node1, node2) { return typeof node1 !== typeof node2 || typeof node1 === ‘string’ && node1 !== node2 || node1.type !== node2.type }
现在,当前的节点有了 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] ); } }
比较子节点
最后,但并非最不重要的是——我们应该遍历这两个节点的每一个子节点并比较它们——实际上为每个节点调用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
- item 1
- hello!
HTML
<button>RELOAD</button> <p></p>
CSS
#root { border: 1px solid black; padding: 10px; margin: 30px 0 0 0; }
打开开发者工具,并观察当按下“Reload”按钮时应用的更改。
总结
现在我们已经编写了虚拟 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!

The main difference between Python and JavaScript is the type system and application scenarios. 1. Python uses dynamic types, suitable for scientific computing and data analysis. 2. JavaScript adopts weak types and is widely used in front-end and full-stack development. The two have their own advantages in asynchronous programming and performance optimization, and should be decided according to project requirements when choosing.

Whether to choose Python or JavaScript depends on the project type: 1) Choose Python for data science and automation tasks; 2) Choose JavaScript for front-end and full-stack development. Python is favored for its powerful library in data processing and automation, while JavaScript is indispensable for its advantages in web interaction and full-stack development.

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

Choosing Python or JavaScript should be based on career development, learning curve and ecosystem: 1) Career development: Python is suitable for data science and back-end development, while JavaScript is suitable for front-end and full-stack development. 2) Learning curve: Python syntax is concise and suitable for beginners; JavaScript syntax is flexible. 3) Ecosystem: Python has rich scientific computing libraries, and JavaScript has a powerful front-end framework.

The power of the JavaScript framework lies in simplifying development, improving user experience and application performance. When choosing a framework, consider: 1. Project size and complexity, 2. Team experience, 3. Ecosystem and community support.

Introduction I know you may find it strange, what exactly does JavaScript, C and browser have to do? They seem to be unrelated, but in fact, they play a very important role in modern web development. Today we will discuss the close connection between these three. Through this article, you will learn how JavaScript runs in the browser, the role of C in the browser engine, and how they work together to drive rendering and interaction of web pages. We all know the relationship between JavaScript and browser. JavaScript is the core language of front-end development. It runs directly in the browser, making web pages vivid and interesting. Have you ever wondered why JavaScr


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

SublimeText3 Chinese version
Chinese version, very easy to use

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

SecLists
SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

SAP NetWeaver Server Adapter for Eclipse
Integrate Eclipse with SAP NetWeaver application server.
