vue3的设计思路(2)渲染器工作原理

我们已经知道 虚拟 DOM 其实就是用 JavaScript 对象来描述真实的 DOM 结构。
那么,虚拟 DOM 是如何变成真实 DOM 并渲染到浏览器页面中的呢?这就用到了我们接下来要介绍的:渲染器。

渲染器的作用就是把 虚拟 DOM 渲染为 真实 DOM,如图 所示:
在这里插入图片描述
渲染器是非常重要的角色,大家平时编写的 Vue.js 组件都是依赖渲染器来工作的。
假设我们有如下虚拟 DOM:

const vnode = {
  tag: "div",
  props: {
    onClick: () => alert("hello"),
  },
  children: "click me",
};

首先简单解释一下上面这段代码:

tag 用来描述标签名称,所以 tag: 'div' 描述的就是一个 <div> 标签。

props 是一个对象,用来描述 <div> 标签的属性、事件等内容。可以看到,我们希望给 div 绑定一个点击事件。

children 用来描述标签的子节点。在上面的代码中,children 是一个字符串值,意思是 div 标签有一个文本子节点:<div>click me</div>

实际上,我们完全可以自己设计虚拟 DOM 的结构,例如可以使用 tagName 代替 tag,因为它本身就是一个 JavaScript 对象,并没有特殊含义。

接下来,我们需要编写一个渲染器,把上面这段虚拟 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 函数接收如下两个参数。

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

接下来,我们可以调用 renderer 函数

renderer(vnode, document.body); // body 作为挂载点

在这里插入图片描述
现在我们回过头来分析渲染器 renderer 的实现思路,总体来说分为三步。

创建元素:把 vnode.tag 作为标签名称来创建 DOM 元素。

为元素添加属性和事件: 遍历 vnode.props 对象,如果 key 以 on 字符开头,说明它是一个事件,把字符 on截取掉后再调用 toLowerCase 函数将事件名称小写化,最终得到合法的事件名称,例如 onClick 会变成 click,最后调用 addEventListener 绑定事件处理函数。

处理 children: 如果 children 是一个数组,就递归地调用 renderer 继续渲染,注意,此时我们要把刚刚创建的元素作为挂载点(父节点);如果 children 是字符串,则使用 createTextNode 函数创建一个文本节点,并将其添加到新创建的元素内。

是不是感觉渲染器并没有想象得那么神秘?其实不然,别忘了我们现在所做的还仅仅是创建节点,渲染器的精髓都在更新节点的阶段。假设我们对 vnode 做一些小小的修改:

const vnode = {
  tag: "div",
  props: {
    onClick: () => alert("hello"),
  },
  children: "click again", // 从 click me 改成 click again
};

对于渲染器来说,它需要精确地找到 vnode 对象的变更点并且只更新变更的内容。就上例来说,渲染器应该只更新元素的文本内容,而不需要再走一遍完整的创建元素的流程。但无论如何,希望大家明白,渲染器的工作原理其实很简单,归根结底,都是使用一些我们熟悉的 DOM 操作 API 来完成渲染工作。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值