Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前沿技术解密——Virtual DOM #1

Open
miniflycn opened this issue Apr 7, 2015 · 14 comments
Open

前沿技术解密——Virtual DOM #1

miniflycn opened this issue Apr 7, 2015 · 14 comments

Comments

@miniflycn
Copy link
Owner

作为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的进行差异渲染。

扩展阅读

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

  1. @Matt-Esch实现的:virtual-dom
  2. 我们自己做的简版实现,用于Mobile页面渲染的:qvd
@miniflycn miniflycn changed the title 前沿技术解密——VirtualDOM 前沿技术解密——Virtual DOM Apr 7, 2015
@litten
Copy link

litten commented Apr 9, 2015

一目鸟然,甚好!可惜github不能点赞

@miniflycn
Copy link
Owner Author

@litten 谢腾爷点赞

@desenmeng
Copy link

👍

@qubaomingg
Copy link

只是讲了整个大致过程,还是没有讲清楚Diff算法的具体做法

@miniflycn
Copy link
Owner Author

@freestyle21
= =不觉得看代码更快么,代码又不是很长。。。。

@luokuning
Copy link

你好,请问React中的component 里的key具体起到什么作用呢?如果没有的话好像也不会让程序产生异常,只会有个警告,那代表key是不是可有可无呢?

@miniflycn
Copy link
Owner Author

@luokuning 以key来辨认data是否是同一个,避免深遍历

@luokuning
Copy link

@miniflycn 那么作用就是能提高性能吗?没有的话也没影响?

@miniflycn
Copy link
Owner Author

@luokuning 没有

@luokuning
Copy link

@miniflycn 明白了,谢谢 :-)

@miniflycn
Copy link
Owner Author

open for meeting

@miniflycn miniflycn reopened this Oct 16, 2015
@time-go
Copy link

time-go commented Jan 11, 2016

非常赞

@time-go
Copy link

time-go commented Jan 11, 2016

我QQ289880020 到时候请教下你

@time-go
Copy link

time-go commented Jan 11, 2016

react diff 完了 后要更新视图上改变部分,怎么样找到要更新的节点

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants