Vue 原理解析概述 (2)

PY
6 min readFeb 11, 2019

--

Vue 資料驅動與渲染畫面

接續上一篇 Vue 原理解析概述 (1) ,了解了從vue int template 到 import vue,接下來這篇會提到 Vue 是如何做資料驅動並渲染畫面。

從實例化到渲染畫面

  1. new Vue

將 Vue 引用進來後,new Vue 實例化鐵定是你開始寫 Vue 的頭一步,實例化等於把一切 Vue 定義的方法進行一個初始化,所以我們找到了 src/core/instance/init.js 裡面定義了所有初始化的方法。

import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { initLifecycle, callHook } from './lifecycle'

包括事件、渲染、生命週期、Data等等的初始化,然後在src/core/instance/index.js 中調用 this._init(options)

2. el:#app

接下來會把掛載點掛上去,Vue是在調用 mount 時才會把掛載點掛上去,所以在 src/platforms/web/entry-runtime-with-compiler.js 中可以找到定義的mount 方法。

Vue.prototype.$mount = function (  
el?: string | Element,
hydrating?: boolean
)

發現 el 除了要掛載的 Dom 元素外,還有一個 hydrating , 而hydrating是啥,這裡可以找到 https://ssr.vuejs.org/zh/guide/hydration.html 簡單來講就是掛載點上帶個 data-server-rendered 這個attribute 如果設置為 true 的話就會實現 SSR。

值得注意的是下面有個判斷式

const options = this.$options  
// resolve template/el and convert to render function
if (!options.render)

其實就是上一篇提到說的當你使用 compiler 模式時,它會自動幫你調用 render 函式,所以這時會發現其實 compiler 模式也只是自動幫你調用這個函式而已,到頭來都會走向 render()。

3. render

這時 new Vue 走到了 render ,在 /src/core/instance/render.js 可以找到這函式,裡面調用了 createElement 這方法,而這方法的目的就是創造熟悉的 Virtual Dom,而在 https://cn.vuejs.org/v2/guide/render-function.html#%E8%99%9A%E6%8B%9F-DOM 這篇也提到了 createElement 的使用方法,至於 Virtual Dom 是什麼,簡單來講目的就是讓 JS 不直接操作 Dom 節點,而是操作相對應由 JS 建立的的 Virtual Dom 結構,這結構會映射到 Dom 節點,以提高效能。

src/core/vdom/create-element.js 找到 createElement 裡面的 VNode 也就是 Virtual Dom ,官方文件上也寫了帶的參數

Ex: https://codepen.io/cifzzpxd/pen/rPdLVy

  1. node: html tag `<div> </div>`, component, 返回上述的異步函數
  2. data: 類似範例中的props實例
  3. Vnode: 第三個參數讓他可以往下再 createElement

Vnode 類別可以在 /src/core/vdom/vnode.js 找到,可以看一下裡面定義的結構,此時 render 跑完,創造了 Virtual dom,準備要 mount 到 畫面上。

4. Vue 渲染流程

看完了 Vue 創造出 Virtual Dom,回過頭來看看 Vue 的渲染流程,從 $mount 開始,$mount 調用了 src/core/instance/lifecycle.js 內的mountComponent,beforeMount 時 vm._render 生成了virtual dom,然後再來 beforeUpdate,宣告一個 updateComponent 裡面會實例化一個渲染的Watcher,用於監聽 Dom 變化,最後 vm._isMounted 設置為true代表已經掛載instance了,就執行mounted函數。
所以總結來說步驟是:
1. beforeMount: 調用 vm._render 生成了 virtual dom
2. beforeUpdate: 宣告一個 updateComponent 用於對 vm._render() 生成的virtual dom進行 watch
3. mounted: vm._isMounted 設置為true

5. update

mount 後是 update ,隨著 Dom 改變來觸發 update ,在 src/core/instance/lifecycle.js 內定義了 _update 並調用 patch 這個方法,這方法會因為平台不同而有不同的調整,web版的在src/platforms/web/runtime/patch.js 內,看到 patchVnode 類別內有oldVnode & vnode ,接下來就是對這兩個做比對然後對應到不同的操作,至於詳細可以參考 https://segmentfault.com/a/1190000008291645 這篇,有 update 到 re-render 怎麼做的。

下面這張圖說明了 watcher 對 render 後產生的 VNode 進行蒐集與綁定最後 update -> patch -> re-render

資料來源: https://cythilya.github.io/2017/04/11/vue-instance/#v-node

總結

來總結一下 new Vue 流程

new Vue -> init -> $mount -> render -> vnode -> dom(el:#app) -> update(patch) -> re-render

--

--