new Vue到底發生了什麼(2.0)

Vue 實例

每一個 Vue 應用都是經過用 Vue 函數建立一個新的 Vue 實例開始的:vue

var vm = new Vue({
  // 選項
})
複製代碼

一個 Vue 應用由一個經過 new Vue 建立的根 Vue 實例,以及可選的嵌套的、可複用的組件樹組成。當一個 Vue 實例被建立時,它將 data 對象中的全部的屬性加入到 Vue 的響應式系統中。當這些屬性的值發生改變時,視圖將會產生「響應」,即匹配更新爲新的值。當這些數據改變時,視圖會進行重渲染。node

初始化及掛載

在 new Vue() 以後。 Vue 會調用 _init 函數進行初始化,也就是這裏的 init 過程,它會初始化生命週期、事件、 props、 methods、 data、 computed 與 watch 等。其中最重要的是經過 Object.defineProperty 設置 setter 與 getter 函數,用來實現「響應式」以及「依賴收集」。算法

初始化以後調用 $mount 會掛載組件,若是是運行時編譯,即不存在 render function 可是存在 template 的狀況,須要進行「編譯」步驟。

mount掛載
vue也可使用render函數,render函數的參數是createElement(h),createElement有三個參數,分別是標籤、attrs屬性對象、內容數組,這樣寫反人性,全部平常仍是使用template。

編譯

compile編譯能夠分紅 parse、optimize 與 generate 三個階段,最終須要獲得 render function。數組

  • parse 會用正則等方式解析 template 模板中的指令、class、style等數據,造成抽象語法樹AST(動態生成語法樹)
  • optimize 的主要做用是標記 static 靜態節點,這是 Vue 在編譯過程當中的一處優化,後面當 update 更新界面時,會有一個 patch 的過程, diff 算法會直接跳過靜態節點,從而減小了比較的過程,優化了 patch 的性能。
  • generate 是將 AST 轉化成 render function 字符串的過程,獲得結果是 render 的字符串以及 staticRenderFns 字符串。

在經歷過 parse、optimize 與 generate 這三個階段之後,組件中就會存在渲染 VNode 所需的 render function 了瀏覽器

響應式

在 init 的時候經過 Object.defineProperty 進行了綁定,它使得當被設置的對象被讀取的時候會執行 getter 函數,而在當被賦值的時候會執行 setter 函數。

當 render function 被渲染的時候,由於會讀取所需對象的值,因此會觸發 getter 函數進行「依賴收集」,「依賴收集」的目的是將觀察者 Watcher 對象存放到當前閉包中的訂閱者 Dep 的 subs 中。造成以下所示的這樣一個關係。bash

在修改對象的值的時候,會觸發對應的 setter, setter 通知以前 「依賴收集」 獲得的 Dep 中的每個 Watcher,告訴它們本身的值改變了,須要從新渲染視圖。這時候這些 Watcher 就會開始調用 update 來更新視圖,固然這中間還有一個 patch 的過程以及使用隊列來異步更新的策略

Virtual DOM

render function 會被轉化成 VNode 節點。Virtual DOM 其實就是一棵以 JavaScript 對象( VNode 節點)做爲基礎的樹,用對象屬性來描述節點,實際上它只是一層對真實 DOM 的抽象。最終能夠經過一系列操做使這棵樹映射到真實環境上。因爲 Virtual DOM 是以 JavaScript 對象爲基礎而不依賴真實平臺環境,因此使它具備了跨平臺的能力,好比說瀏覽器平臺、Weex、Node 等。閉包

{
    tag: 'div',                 /*說明這是一個div標籤*/
    children: [                 /*存放該標籤的子節點*/
        {
            tag: 'a',           /*說明這是一個a標籤*/
            text: 'click me'    /*標籤的內容*/
        }
    ]
}
複製代碼

渲染後能夠獲得dom

<div>
    <a>click me</a>
</div>
複製代碼

更新視圖

修改一個對象值的時候,會經過 setter -> Watcher -> update 的流程來修改對應的視圖,那麼最終是如何更新視圖的呢?

當數據變化後,執行 render function 就能夠獲得一個新的 VNode 節點,咱們若是想要獲得新的視圖,最簡單粗暴的方法就是直接解析這個新的 VNode 節點,而後用 innerHTML 直接所有渲染到真實 DOM 中。可是其實咱們只對其中的一小塊內容進行了修改,這樣作彷佛有些「浪費」。異步

那麼咱們爲何不能只修改那些 「改變了的地方」 呢?這個時候就要介紹咱們的「patch」了。咱們會將新的 VNode 與舊的 VNode 一塊兒傳入 patch 進行比較,通過 diff 算法得出它們的 「差別」 。最後咱們只須要將這些 「差別」 的對應 DOM 進行修改便可。函數

事實上。vue2並無實現節點上vdom更新,在vue3上纔有望實現(2019vueconf大會尤雨奚)

new vue 初始化-mounted掛載-compile-render-createElement虛擬節點-(返回普通vnode或者createcomponents建立一個組件vnode,這個節點是vue的一個子類,總之返回的都是vnode,這個vnode可能有若干個子節點,它們也是vnode類型,這裏就能夠描述爲vnode-tree)-update->patch(createElm做用是,若是vnode是普通的節點就建立真實的dom節點插入父元素下,若是節點vnode 的tag標明是組件,根據vnode建立一個組件節點,而且執行相應的鉤子函數,這個過程就是遍歷全部的子vnode,若是它的子節點vnode又是個組件,重複剛纔的過程render-建立虛擬節點,這個過程是一個深度優先遍歷的算法。

相關文章
相關標籤/搜索