1. 程式人生 > >vue的原始碼學習之五——5.資料驅動(Virtual DOM)

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

 這麼一個 Class 去描述,它是定義在 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 等過程。