vue的原始碼學習之五——5.資料驅動(Virtual DOM)
1. 介紹
版本:2.5.17。
我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。
學習文件:https://ustbhuangyi.github.io/vue-analysis/data-driven/virtual-dom.html
2. Virtual DOM
Vue 2.0 相比 Vue 1.0 最大的升級就是利用了 Virtual DOM
2.1 原生DOM
Virtual DOM 這個概念相信大部分人都不會陌生,它產生的前提是瀏覽器中的 DOM 是很“昂貴"的,為了更直觀的感受,我們可以簡單的把一個簡單的 div 元素的屬性都打印出來,如圖所示:
可以看到,真正的 DOM 元素是非常龐大的,因為瀏覽器的標準就把 DOM 設計的非常複雜。當我們頻繁的去做 DOM 更新,會產生一定的效能問題。
2.2 Virtual DOM的出現
Virtual DOM 就是用一個原生的 JS 物件去描述一個 DOM 節點,所以它比建立一個 DOM 的代價要小很多。在 Vue.js 中,Virtual DOM 是用 VNode
src/core/vdom/vnode.js
中的。
/* @flow */ 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 } } export const createEmptyVNode = (text: string = '') => { const node = new VNode() node.text = text node.isComment = true return node } export function createTextVNode (val: string | number) { return new VNode(undefined, undefined, undefined, String(val)) } // optimized shallow clone // used for static nodes and slot nodes because they may be reused across // multiple renders, cloning them avoids errors when DOM manipulations rely // on their elm reference. export function cloneVNode (vnode: VNode): VNode { const cloned = new VNode( vnode.tag, vnode.data, vnode.children, vnode.text, vnode.elm, vnode.context, vnode.componentOptions, vnode.asyncFactory ) cloned.ns = vnode.ns cloned.isStatic = vnode.isStatic cloned.key = vnode.key cloned.isComment = vnode.isComment cloned.fnContext = vnode.fnContext cloned.fnOptions = vnode.fnOptions cloned.fnScopeId = vnode.fnScopeId cloned.asyncMeta = vnode.asyncMeta cloned.isCloned = true return cloned }
可以看到 Vue.js 中的 Virtual DOM 的定義還是略微複雜一些的,因為它這裡包含了很多 Vue.js 的特性。這裡千萬不要被這些茫茫多的屬性嚇到,實際上 Vue.js 中 Virtual DOM 是借鑑了一個開源庫 snabbdom的實現,然後加入了一些 Vue.js 特色的東西。我建議大家如果想深入瞭解 Vue.js 的 Virtual DOM 前不妨先閱讀這個庫的原始碼,因為它更加簡單和純粹。
3. 總結
其實 VNode 是對真實 DOM 的一種抽象描述,它的核心定義無非就幾個關鍵屬性,標籤名、資料、子節點、鍵值等,其它屬性都是都是用來擴充套件 VNode 的靈活性以及實現一些特殊 feature 的。由於 VNode 只是用來對映到真實 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常輕量和簡單的。
Virtual DOM 除了它的資料結構的定義,對映到真實的 DOM 實際上要經歷 VNode 的 create、diff、patch 等過程。