[vue] What is the principle of virtual dom? Realize virtual dom by handwriting!

 1. The principle of virtual dom

Virtual DOM is an abstraction of DOM.Essentially, it uses JavaScript objects to describe the DOM structure. The implementation of virtual DOM in Vue.js mainly involves the following steps:

1. Generate virtual DOM:
   Vue.js uses the `render` function to generate virtual DOM based on the template code. During this process, each HTML tag will be converted into a VirtualNode object, which will record the tag's type, attributes, child nodes and other information.

2. Compare the old and new virtual DOM: 
   When the data changes, Vue.js will regenerate the new virtual DOM and then compare it with the old virtual DOM (this process is called for Diff). Through the Diff algorithm, Vue can accurately find the difference between two virtual DOMs.

3. Partially update the real DOM: 
   Based on the comparison results, Vue will directly calculate the minimum part of the DOM that needs to be updated, and apply these changes to the real DOM. This idea can minimize operations on the DOM, thus improving performance.


2. Implement virtual dom (VirtualNode) by handwriting 

The following is a simplified example of using JavaScript to create and manipulate a virtual DOM :

// 创建虚拟 DOM 对象
function createElement(tag, props, children) {
  return {
    tag,
    props,
    children
  }
}


// 渲染虚拟 DOM 到真实环境
function render(vnode, container) {
    var el;
  
    // 创建节点
    // 针对文本节点进行特殊处理
    if (typeof vnode === 'string' || typeof vnode === 'number') {
        el = document.createTextNode(vnode);
    }
    else {
        el = document.createElement(vnode.tag);
      
        // 设置 DOM 属性
        for (var key in vnode.props) {
            el.setAttribute(key, vnode.props[key]);
        }
  
        // 如果有子节点,递归调用 render 函数
        if (vnode.children) {
            vnode.children.forEach(child => {
                render(child, el); // 注意这里使用 el 作为容器
            });
        }
    }
    
    // 将生成的真实 DOM 挂载到 container 上
    container.appendChild(el);
}

// 创建虚拟 DOM
const vnode = createElement('div', { id: 'app' }, [createElement('div', {}, ['Hello, Virtual DOM'])]);

// 渲染到真实 DOM
render(vnode, document.body);

summary:

The above code mainly shows how to create a virtual DOM object and implement rendering from virtual DOM to real DOM. In real Vue.js, the implementation of virtual DOM will be more complex, including optimization measures such as Diff algorithm and batch asynchronous updates.


On the basis of the above, let us have a deeper understanding of the diff algorithm and patch process of Virtual DOM.

The main purpose of these two links is to compare the differences between the old and new virtual DOM, find the minimum modifications, and apply these modifications to the real DOM to improve application performance.

Let’s use examples to explain: (ps: my ideas are recorded in the comments in the code)

// 更新函数,用于比较新旧虚拟节点(vnode)的差异,并将差异应用到实际的 DOM 元素上。
function updateElement(vnode, oldVnode) {

    // 如果旧节点和新节点相同,那么什么都不做。
    if (vnode === oldVnode) return;

    // 如果有新的文本内容,那么更新文本内容。
    if (vnode.text) {
        oldVnode.text = vnode.text;
    }

    // 更新节点属性
    // el 是新旧节点共享的真实DOM元素
    var el = oldVnode.el = vnode.el;

    // oldProps 是旧虚拟节点的属性集合
    var oldProps = oldVnode.props;
    // props 是新虚拟节点的属性集合
    var props = vnode.props;

    // 如果旧属性在新属性集合中不存在,那么在真实 DOM 上移除这个属性。
    for (var key in oldProps) {
        if (!props.hasOwnProperty(key)) {
            el.removeAttribute(key);
        }
    }

    // 对比新旧属性集合,如果不相等,那么在真实 DOM 上更新这个属性。
    for (var key in props) {
        if (props[key] !== oldProps[key]) {
            el.setAttribute(key, props[key]);
        }
    }

    // 比较和更新子节点,此处简化为重新渲染所有子节点
    // oldChildren 是旧虚拟节点的子节点集合
    var oldChildren = oldVnode.children || [];
    // children 是新虚拟节点的子节点集合
    var children = vnode.children || [];

    // 逐个对比更新子节点
    for (var i = 0; i < children.length || i < oldChildren.length; i++) {
        // 递归调用 render 函数来处理子节点的更新,这里假设 render 函数会处理子节点的渲染和更新
        render(children[i], el, oldChildren[i]);
    }
}

The updateElement function is a very simplified example that checks vnode for differences against oldVnode and applies those differences to the actual DOM element. Then it processes child nodes recursively. Please note that this function does not handle the situation of different node types, nor does it implement the optimization of key attributes and list rendering. These problems are all that the diff algorithm of Vue.js needs to solve.

In actual Vue.js, the virtual DOM update process will be more optimized and complex. For example, the patch process will put the modification operation into the queue and execute it in the next event loop. At the same time, the same continuous operation will be merged to achieve the highest performance. In addition, the diff algorithm of Vue.js will also look for the same parts in the old and new vnode lists based on the key attribute of the list rendering to achieve the effect of minimizing DOM operations. Moreover, when the component is re-rendered, Vue.js will use static node tags to avoid unnecessary comparisons and renderings to further improve performance.

ps: About the diff algorithm and other content will be written in the next blog.

Guess you like

Origin blog.csdn.net/wanghaoyingand/article/details/134336394