【vue】虚拟dom的原理是什么?手写实现虚拟dom !

1.虚拟dom的原理

虚拟 DOM 是对 DOM 的抽象,本质上就是用 JavaScript 对象来描述 DOM 结构。Vue.js 中关于虚拟 DOM 的实现主要进行了以下几个步骤:

1.生成虚拟 DOM:

Vue.js 使用 `render` 函数来依据模板代码生成虚拟 DOM。在这个过程中,每一个 HTML 标签都将被转换成一个 VirtualNode 对象,这个对象会记录该标签的类型、属性、子节点等信息。

  1. 对比新旧虚拟 DOM:

当数据发生变化时,Vue.js 会重新生成新的虚拟 DOM,然后与旧的虚拟 DOM 进行对比(这个过程称为 Diff)。通过 Diff 算法,Vue 可以精确找出两个虚拟 DOM 之间的区别。

  1. 局部更新真实 DOM:

根据对比结果,Vue 会直接计算出最小需要更新的 DOM 部分,并将这些变化应用到真实的 DOM 上。这种思路能够最大程度地减少对 DOM 的操作,从而提升性能。


2.手写实现虚拟dom(VirtualNode)

以下是使用 JavaScript 创建和操作虚拟 DOM的简化版本例子:

javascript 复制代码
// 创建虚拟 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);

小结:

以上代码主要展示了如何创建虚拟 DOM 对象,并实现虚拟 DOM 到真实 DOM 的渲染。在真实的 Vue.js 中,虚拟 DOM 的实现会更加复杂,包括 Diff 算法、批量异步更新等优化措施。


在上面的基础上,让我们更深入理解 Virtual DOM (虚拟 DOM) 的 diff 算法和 patch 过程。
这两个环节主要做的是比较新老虚拟 DOM 的差异,并找出最少的修改,应用这些修改到真实 DOM 上,以提高应用的性能。

还是用示例来解释:(ps:我的思路记录在了代码中的注释上)

javascript 复制代码
// 更新函数,用于比较新旧虚拟节点(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]);
    }
}

这个 updateElement 函数是一个非常简化的例子,它检查 vnode 与 oldVnode 的差异,并将这些差异应用到实际的 DOM 元素上。然后它递归处理子节点。请注意,这个函数并没有处理节点类型不同的情况,也没有实现 key 属性和列表渲染的优化,这些问题都是 Vue.js 的 diff 算法需要解决的。

在实际的 Vue.js 中,虚拟 DOM 的更新过程会更加优化和复杂。比如 patch 过程会将修改操作放入队列,并在下一个事件循环中执行,同时对连续的同样操作进行合并,以达到最高的性能。此外,Vue.js 的 diff 算法也会根据列表渲染的 key 属性,寻找新旧 vnode 列表中相同的部分,以达到最小化 DOM 操作的效果。而且,当组件重新渲染时,Vue.js 会通过静态节点标记,避免不必要的比较和渲染,以进一步提升性能。

ps: 关于diff算法以及其他内容将会在下一篇博客中编写。

相关推荐
五号厂房14 分钟前
仿照AntDesign,实现一个自定义Tab
前端
Bob999821 分钟前
三大浏览器(Firefox、Opera、Chrome)多个Profile管理!
开发语言·javascript·eclipse·sqlite·ecmascript·hbase
Frankabcdefgh29 分钟前
前端面试 js
开发语言·javascript·原型模式
浏览器爱好者39 分钟前
如何删除Google Chrome中的所有历史记录【一键清除】
前端·chrome
埃兰德欧神40 分钟前
三分钟让你的H5变身‘伪原生’,揭秘H5秒变应用的魔法配置
javascript·html·产品
米开朗基杨41 分钟前
Cursor 最强竞争对手来了,专治复杂大项目,免费一个月
前端·后端
Lonwayne42 分钟前
Web服务器技术选型指南:主流方案、核心对比与策略选择
运维·服务器·前端·程序那些事
学习机器不会机器学习1 小时前
深入浅出JavaScript常见设计模式:从原理到实战(1)
开发语言·javascript·设计模式
hax1 小时前
deepseek-R1 理解代码能力一例
javascript·deepseek
brzhang1 小时前
效率神器!TmuxAI:一款无痕融入终端的AI助手,让我的开发体验翻倍提升
前端·后端·算法