前端开发者必看:虚拟DOM渲染器的内部机制

22 篇文章 0 订阅
4 篇文章 0 订阅

前言

众所周知,直接操作DOM是一个昂贵的操作,会销毁所有DOM元素,再全量创建新的DOM元素。

而虚拟DOM的意义就是找出差异的性能消耗最小化,通过减少直接的DOM操作来提高性能,而不是重新渲染整个页面。

渲染器的作用就是递归地遍历虚拟DOM对象,并调用原生DOM API来完成真实DOM的创建,将虚拟DOM转化为真实DOM。

介绍

假设我们有如下虚拟DOM:

const vnode = {
    tag: 'div', 
    props: {
        onClick: () => console.log('hello!')
    },
    children: 'click me'       
}
  1. tag 用来描述标签名称,所以 tag: 'div' 描述的就是一个 <div> 标签。
  2. props 是一个对象,用来描述 <div> 标签的属性、事件等内容。可以看到,我们希望给 div 绑定一个点击事件。
  3. children 用来描述标签的子节点。在上面的代码中,children 是一个字符串值,意思是 div 标签有一个文本子节点:<div>click me</div>

接下来,我们需要编写一个渲染器,把上面这段虚拟DOM渲染为真实DOM。

代码实现

function renderer(vnode, container) {
    // 使用 vnode.tag 作为标签名称创建 DOM 元素
    const el = document.createElement(vnode.tag);
    // 遍历 vnode.props,将属性、事件添加到 DOM 元素
    for (const key in vnode.props) {
        if (/^on/.test(key)) {
            // 如果 key 以 on 开头,说明它是事件
            el.addEventListener(
                key.substr(2).toLowerCase(), // 事件名称 onClick --> click
                vnode.props[key] // 事件处理函数
            );
        }
    }
    // 处理 children
    if (typeof vnode.children === 'string') {
        // 如果 children 是字符串,说明它是元素的文本子节点
        el.appendChild(document.createTextNode(vnode.children));
    } else if (Array.isArray(vnode.children)) {
        // 递归地调用 renderer 函数渲染子节点,使用当前元素 el作为挂载点
        vnode.children.forEach(child => renderer(child, el));
    }
    // 将元素添加到挂载点下
    container.appendChild(el);
}

这里的 renderer 函数接收如下两个参数:

  1. vnode: 虚拟DOM对象。
  2. container: 一个真实DOM元素,作为挂载点,渲染器会把虚拟DOM挂载到这个点下。

代码说明

总体来说,实现一个简单的渲染器分为三步:

  1. 创建元素:把 vnode.tag 作为标签名称来创建DOM元素。
  2. 为元素添加属性和事件:遍历 vnode.props 对象,如果 keyon 字符开头,说明它是一个事件,把字符 on 截取掉后再调用 toLowerCase 函数将事件名称小写化,最终得到合法的事件名称,例如 onclick 会变成 click,最后调用 addEventListener 绑定事件处理函数。
  3. 处理children:如果 children 是一个数组,就递归地调用 renderer 继续渲染,注意,此时我们要把刚刚创建的元素作为挂载点(父节点);如果 children 是字符串,则使用 createTextNode 函数创建一个文本节点,并将其添加到新创建的元素内。

总结

当然,vnode.tag 也可以是组件,组件的本质就是一组DOM元素的封装。

在生成的虚拟DOM对象中多出了一个 patchFlags 属性,我们假设数字1代表“class是动态的”,这样渲染器看到这个标志时就知道:“哦,原来只有class属性会发生改变。”

它会通过Diff算法找出变更点,并且只会更新需要更新的内容。

就相当于省去了寻找变更点的工作量,性能自然就提升了。通过这篇文章,我们了解了虚拟DOM和渲染器的基本概念,以及如何实现一个简单的渲染器。

– 欢迎点赞、关注、转发、收藏【我码玄黄】,各大平台同名。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我码玄黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值