iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 4
0
Modern Web

技術在走,Vue.js 要有系列 第 4

|D4| 從原始碼看 Vue 渲染機制 (2) - Virtual DOM

前言

|D3|從 vue 原始碼看 new Vue 做了什麼中提到,當 new Vue 後這個 Vue 實例就有了初始化的屬性或方法。

接著要使用 Vue 實例的私有方法 _render function ,把實例渲染成一個虛擬 DOM。

了解虛擬 DOM 之後再來看 _render function。

Virtual DOM

回顧一下,瀏覽器解析 HTML 構成 DOM tree,DOM tree 結構由各個節點( node )組成。

圖片來源:google-developers

以 div 元素為例,用 console.dir 把這個 DOM 所有 properties 印出來(只截圖部分而已)。
可以看到 DOM 非常龐大,每當用原生 JS 操作 DOM 時,瀏覽器會再次構成 DOM tree 重新渲染。

Virtual DOM 解決什麼問題

Virtual DOM 不是真正的 DOM,概念是當 data 狀態發生變化,Virtual DOM 會進行 diff 運算,只重新渲染需要被取代的 DOM,非常輕量。

Virtual DOM 是什麼

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
  devtoolsMeta: ?Object; // used to store functional render context for devtools
  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
  }
}

Virtual DOM 用 VNode Class 描述基礎的 VNode

  • elm: 當前虛擬節點相對應的真實 DOM 節點
  • data: 包含 class、id等 HTML 屬性

VNodeData 的定義可從下面 2 中知道

  1. types/vnode.d.ts
export interface VNodeData {
  key?: string | number;
  slot?: string;
  scopedSlots?: { [key: string]: ScopedSlot | undefined };
  ref?: string;
  refInFor?: boolean;
  tag?: string;
  staticClass?: string;
  class?: any;
  staticStyle?: { [key: string]: any };
  style?: string | object[] | object;
  props?: { [key: string]: any };
  attrs?: { [key: string]: any };
  domProps?: { [key: string]: any };
  hook?: { [key: string]: Function };
  on?: { [key: string]: Function | Function[] };
  nativeOn?: { [key: string]: Function | Function[] };
  transition?: object;
  show?: boolean;
  inlineTemplate?: {
    render: Function;
    staticRenderFns: Function[];
  };
  directives?: VNodeDirective[];
  keepAlive?: boolean;
}

舉個例子

<div id="app">
    <p class="text">{{ message }}</p>
    <ul>
        <li v-for="user of users" class="item">{{ user }}</li>
    </ul>
</div>

<scrip>
     new Vue({
        el: '#app',
        data: {
            message: 'hello',
            users: ['Tom', 'Tony', 'Tim']
        }
    })
</scrip>

node tree

{
    'tag': 'div'
    'data': {
        'attrs': { 'id': 'app' }
    },
    'children': [
        {
            'tag': 'p',
            'data': {
                'staticClass': 'text'
            }
            'text': 'hello'
        },
        {
            'tag': 'ul',
            'children': [
                {
                    'tag': 'li',
                    'data': {
                        'staticClass': 'item'
                    }
                    'text': 'Tom'
                },
                {
                    'tag': 'li',
                    'data': {
                        'staticClass': 'item'
                    }
                    'text': 'Tony'
                },
                {
                    'tag': 'li',
                    'data': {
                        'staticClass': 'item'
                    }
                    'text': 'Tim'
                }
            ]
        }
    ]
}


上一篇
|D3| 從原始碼看 Vue 渲染機制 (1) - new Vue 做了什麼
下一篇
|D5| 從原始碼看 Vue 渲染機制 (3) - createElement
系列文
技術在走,Vue.js 要有30

1 則留言

我要留言

立即登入留言