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 的过程,要经历 VNode 的 create、diff、patch 等过程。在 Vue 中,create 是通过 createElement 方法创建的,我们接下来分析这部分的实现。