React 解密1:Virtual DOM

发布于 2021-11-01 12:49:10 字数 3251 浏览 1065 评论 0

作为 React 的核心技术之一 Virtual DOM,一直披着神秘的面纱。

实际上,Virtual DOM 包含:

  1. JavaScript DOM 模型树(VTree),类似文档节点树(DOM)
  2. DOM 模型树转节点树方法(VTree -> DOM)
  3. 两个 DOM 模型树的差异算法(diff(VTree, VTree) -> PatchObject)
  4. 根据差异操作节点方法(patch(DOMNode, PatchObject) -> DOMNode)

接下来我们分别探讨这几个部分:

VTree

VTree 模型非常简单,基本结构如下:

{
    // tag的名字
    tagName: 'p',
    // 节点包含属性
    properties: {
        style: {
            color: '#fff'
        }
    },
    // 子节点
    children: [],
    // 该节点的唯一表示,后面会讲有啥用
    key: 1
}

所以我们很容易写一个方法来创建这种树状结构,例如 React 是这么创建的:

// 创建一个div
react.createElement('div', null, [
    // 子节点img
    react.createElement('img', { src: "avatar.png", class: "profile" }),
    // 子节点h3
    react.createElement('h3', null, [[user.firstName, user.lastName].join(' ')])
]);

VTree -> DOM

这方法也不太难,我们实现一个简单的:

function create(vds, parent) {
  // 首先看看是不是数组,如果不是数组统一成数组
  !Array.isArray(vds) && (vds = [vds]);
  //  如果没有父元素则创建个fragment来当父元素
  parent = parent || document.createDocumentFragment();
  var node;
  // 遍历所有VNode
  vds.forEach(function (vd) {
    // 如果VNode是文字节点
    if (isText(vd)) {
      // 创建文字节点
      node = document.createTextNode(vd.text);
    // 否则是元素
    } else {
      // 创建元素
      node = document.createElement(vd.tag);
    }
    // 将元素塞入父容器
    parent.appendChild(node);
    // 看看有没有子VNode,有孩子则处理孩子VNode
    vd.children && vd.children.length &&
      create(vd.children, node);

    // 看看有没有属性,有则处理属性
    vd.properties &&
      setProps({ style: {} }, vd.properties, node);
  });
  return parent;
}

diff(VTree, VTree) -> PatchObject

差异算法是 Virtual DOM 的核心,实际上该差异算法是个取巧算法(当然你不能指望用O(n^3)的复杂度来解决两个树的差异问题吧),不过能解决Web的大部分问题。

那么 React 是如何取巧的呢?

分层对比

如图,React 仅仅对同一层的节点尝试匹配,因为实际上,Web 中不太可能把一个 Component 在不同层中移动。

基于 key 来匹配

还记得之前在 VTree 中的属性有一个叫 key 的东东么?这个是一个 VNode 的唯一识别,用于对两个不同的 VTree 中的 VNode 做匹配的。

这也很好理解,因为我们经常会在 Web 遇到拥有唯一识别的 Component(例如课程卡片、用户卡片等等)的不同排列问题。

基于自定义元素做优化

React 提供自定义元素,所以匹配更加简单。

patch(DOMNode, PatchObject) -> DOMNode

由于 diff 操作已经找出两个 VTree 不同的地方,只要根据计算出来的结果,我们就可以对 DOM 的进行差异渲染。

扩展阅读

具体可参考下面两份代码实现:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

虐人心

有一天你能到我的心里去,你会看到那里全是你给的伤悲。

0 文章
0 评论
24515 人气
更多

推荐作者

已经忘了多久

文章 0 评论 0

15867725375

文章 0 评论 0

LonelySnow

文章 0 评论 0

走过海棠暮

文章 0 评论 0

轻许诺言

文章 0 评论 0

信馬由缰

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文