【Vue源码】5.Virtual DOM

107 阅读2分钟

Virtual DOM

4._render 中我们提到了 Virtual DOM,接下来了解一下 Virtual DOM是什么。

Virtual DOM 很多人都不会陌生,他产生的前提是浏览器的 DOM 和其操作都是非常 昂贵 的,所以我们用 JS 对象来描述一个 DOM 节点。

有兴趣的同学可以在控制台执行一下代码查看一个浏览器 DOM 包含的内容,或者直接查看 Element 定义

const div = document.createElement("div");
for (let key in div) {
  console.log(key + ":" + div[key]);
}

Vue 中,Virtual DOM 用一个 VNode Class 来描述,他定义在 src/core/vdom/vnode.js 中。

export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node

  // strictly internal
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?
  asyncFactory: Function | void; // async component factory function
  asyncMeta: Object | void;
  isAsyncPlaceholder: boolean;
  ssrContext: Object | void;
  fnContext: Component | void; // real context vm for functional nodes
  fnOptions: ?ComponentOptions; // for SSR caching
  fnScopeId: ?string; // functional scope id support

  constructor(
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag;
    this.data = data;
    this.children = children;
    this.text = text;
    this.elm = elm;
    this.ns = undefined;
    this.context = context;
    this.fnContext = undefined;
    this.fnOptions = undefined;
    this.fnScopeId = undefined;
    this.key = data && data.key;
    this.componentOptions = componentOptions;
    this.componentInstance = undefined;
    this.parent = undefined;
    this.raw = false;
    this.isStatic = false;
    this.isRootInsert = true;
    this.isComment = false;
    this.isCloned = false;
    this.isOnce = false;
    this.asyncFactory = asyncFactory;
    this.asyncMeta = undefined;
    this.isAsyncPlaceholder = false;
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next */
  get child(): Component | void {
    return this.componentInstance;
  }
}

可以看到类 VNode 上有很多属性,其中一些属性是专门针对与 Vue 相关功能的实现,更简洁的 Virtual DOM 可以参考开源库 snabbdom 的实现。

总结

VNode 是对真实 DOM 的一种描述,他更简洁直观,标签名、数据、子节点、键值等就可以描述一个浏览器 DOM 结构,其他属性都是用来扩展以及实现一些特殊的功能的。由于 VNode 只是用来映射真实的 DOM,并不包含 DOM 操作,因此他非常的轻量和简单。

VNode 映射到真实的浏览器 DOM 的过程,要经历 VNodecreatediffpatch 等过程。在 Vue 中,create 是通过 createElement 方法创建的,我们接下来分析这部分的实现。

源码分析 GitHub 地址