1. 程式人生 > >vue原始碼學習——虛擬dom樹是如何定義的

vue原始碼學習——虛擬dom樹是如何定義的

情景:相信通過前面的學習你已經知道了虛擬dom為什麼會被構思,那麼接下來你好奇的應該是作者該如何定義這個虛擬dom

export default class VNode {
  tag: string | void;//當前節點的標籤名
  data: VNodeData | void;//當前節點對應的物件,包含了一些具體的資料資訊,是一個VNodeData型別,可以參考VNodeData型別中的資料資訊
  children: ?Array<VNode>;//當前節點的子節點是一個數組
  text: string | void;//當前節點的文字
  elm: Node | void;//當前虛擬節點對應的真實dom節點
  ns: string | void;//當前節點的名字空間
  context: Component | void; // rendered in this component's scope 當前節點的編譯作用域
  functionalContext: Component | void; // only for functional component root nodes函式化元件作用域
  key: string | number | void;//節點的key屬性,被當作節點的標誌,用以優化
  componentOptions: VNodeComponentOptions | void;//元件的option選項
  componentInstance: Component | void; // component instance 當前節點對應的元件的例項
  parent: VNode | void; // component placeholder node 當前節點的父節點 
  raw: boolean; // contains raw HTML? (server only) 是否為原生html或只是普通文字,innerHTML的時候為true,textContent的時候為false
  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? //是否有v-once屬性
  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions
  ) {
    /*當前節點的標籤名*/
    this.tag = tag
    /*當前節點對應的物件,包含了具體的一些資料資訊,是一個VNodeData型別,可以參考VNodeData型別中的資料資訊*/
    this.data = data
    /*當前節點的子節點,是一個數組*/
    this.children = children
    /*當前節點的文字*/
    this.text = text
    /*當前虛擬節點對應的真實dom節點*/
    this.elm = elm
    /*當前節點的名字空間*/
    this.ns = undefined
    /*編譯作用域*/
    this.context = context
    /*函式化元件作用域*/
    this.functionalContext = undefined
    /*節點的key屬性,被當作節點的標誌,用以優化*/
    this.key = data && data.key
    /*元件的option選項*/
    this.componentOptions = componentOptions
    /*當前節點對應的元件的例項*/
    this.componentInstance = undefined
    /*當前節點的父節點*/
    this.parent = undefined
    /*簡而言之就是是否為原生HTML或只是普通文字,innerHTML的時候為true,textContent的時候為false*/
    this.raw = false
    /*靜態節點標誌*/
    this.isStatic = false
    /*是否作為跟節點插入*/
    this.isRootInsert = true
    /*是否為註釋節點*/
    this.isComment = false
    /*是否為克隆節點*/
    this.isCloned = false
    /*是否有v-once指令*/
    this.isOnce = false
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next */
  get child (): Component | void {
    return this.componentInstance
  }
}
  • 具體舉例應用
{
    tag: 'div',
    data: {
        class: 'test'
    },
    children: [
        {
            tag: 'span',
            data: {
                class: 'demo'
            }
            text: 'hello'
        }
    ]
}

 渲染的結果

<div class="test">
    <span class="demo">hello</span>
</div>
  •  建立一個空節點的方法(一個空節點對頁面沒有任何影響)
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))
//一般構造器裡傳8個引數
}
  • cloneVNode克隆一個VNode節點
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
//構造器裡面有7個引數,這個引數不是必傳的為何出現在這裡
  )
  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
//下面的引數沒有ssrContext、isAsyncPlaceholder、isOnce、isRootInsert、raw
}
  • 雖然原始碼的這個vnode.js看懂了,可是作者的構造器和克隆函式為什麼要這麼設計呢

   這個問題還需要進一步研究,繼續加油

  • 或許你會問這個節點為什麼就能在頁面上顯示出來

    當然還有一部分js操作了html的程式碼沒有被展示出來,如果你還想繼續瞭解可以再繼續研究原始碼