vue.js原理初探

vue.js是一個很是優秀的前端開發框架,不是我說的,你們都知道。本人也使用過vue.js開發過移動端SPA應用,仍是學習階段,經驗尚淺,能力有限。不過我也懂得只會使用輪子不知因此然是遠遠不夠的,憑本身淺薄的見識,斗膽寫一篇略微深刻的一點文章。html

首先我如今的能力,獨立閱讀源碼仍是有很大壓力的,所幸vue寫的很規範,經過方法名基本能夠略知一二,裏面的原理不懂的地方多方面查找資料,本文中不規範不正確的地方歡迎指正,學生很是願意接受各位前輩提出寶貴的建議和指導。前端

寫這篇文章時GitHub上vue最新版是v2.5.13,採用了flow做爲類型管理工具,關於flow相關內容選擇性忽略了,不考慮類型系統,只考慮實現原理,寫下這篇文章。vue

本文大概涉及到vue幾個核心的地方:vue實例化,虛擬DOM,模板編譯過程,數據綁定。node

下圖爲最新版本vue的生命週期react

vue實例化

首先從建立vue實例開始,vue的構造函數在src/core/instance/index.js文件中,不過在src/core/index.js中對其進行了一系列處理,其中關於服務器環境渲染等相關內容在此不作討論。這裏有initGlobalAPI方法在src/core/global-api/index.js中,此方法初始化了一些vue提供的的全局方法,set,delete,nextTick等等,並初始化了和處理mixins,extends等相關功能的方法。如今回過來從全局來看src/core/instance/index.js,在其中還包括幾個方法,它們初始化了vue原型上面提供的一些方法,而vue的構造函數中調用的就是原型上面的_init方法。git

研究vue的實例化就要研究_init方法,此方法定義在src/core/instance/init.js下的initMixin中,裏面是對vue實例即vm的處理。其中包括開發環境下的代理配置等一些列處理,並處理了傳遞給構造函數的參數等,重點在一系列方法github

initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

其實從名字就能看出這些方法都是作什麼的:初始化生命週期,初始化事件,初始化渲染,觸發執行beforeCreate生命週期方法,初始化data/props數據監聽,觸發執行created生命週期方法。web

此時,對應到生命週期示例圖,created方法執行結束,接下來判斷是否傳入掛載的el節點,若是傳入的話此時就會經過$mount函數把組件掛載到DOM上面,整個vue構造函數就執行完成了。以上是vue對象建立的基本流程,其中有幾個重要的關鍵點也是vue的核心所在,下面來重點探討一下。算法

模板編譯

上面提到了掛載的$mount函數,此函數的實現與運行環境有關,在此只看web中的實現。該方法在src/platforms/web/runtime/index.js中定義,掛載在vue的原型上。實現只有簡單的兩行,判斷運行環境爲瀏覽器,調用工具方法查找到el對應的DOM節點,再調用位於src/core/instance/lifecycle.js下的mountComponent方法來實現掛載,這裏就涉及到了掛載以前的處理問題。對於擁有render(JSX)函數的狀況,組件能夠直接掛載,若是使用的是template,須要從中提取AST渲染方法(注意若是使用構建工具,最終會爲咱們編譯成render(JSX)形式,因此無需擔憂性能問題),AST即抽象語法樹,它是對真實DOM結構的映射,可執行,可編譯,可以把每一個節點部分都編譯成vnode,組成一個有對應層次結構的vnode對象。有了渲染方法,下一步就是更新DOM,注意並非直接更新,而是經過vnode,因而涉及到了一個很是重要的概念。api

虛擬DOM

虛擬DOM技術是一個很流行的東西,現代前端開發框架vue和react都是基於虛擬DOM來實現的。虛擬DOM技術是爲了解決一個很重要的問題:瀏覽器進行DOM操做會帶來較大的開銷。

操做DOM是不可避免的,常規的操做也不會有任何問題,可是經驗不足的開發者每每很容易寫出大量的多餘或重複的DOM操做,成爲前端性能優化中重要的問題。想提高效率,咱們就要儘量減小DOM操做,只修改須要修改的地方。要知道js自己運行速度是很快的,而js對象又能夠很準確地描述出相似DOM的樹形結構,基於這一前提,人們研究出一種方式,經過使用js描述出一個假的DOM結構,每次數據變化時候,在假的DOM上分析數據變化先後結構差異,找出這個最小差異而且在真實DOM上只更新這個最小的變化內容,這樣就極大程度上下降了對DOM的操做帶來的性能開銷。

上面的假的DOM結構就是虛擬DOM,比對的算法成爲diff算法,這是實現虛擬DOM技術的關鍵,在vue初始化時,首先用JS對象描述出DOM樹的結構,用這個描述樹去構建真實DOM,並實際展示到頁面中,一旦有數據狀態變動,須要從新構建一個新的JS的DOM樹,對比兩棵樹差異,找出最小更新內容,並將最小差別內容更新到真實DOM上。

有了虛擬DOM,下面一個問題就是,何時會觸發更新,接下來要介紹的,就是vue中最具特點的功能--數據響應系統及實現。

數據綁定

記得vue.js的做者尤雨溪老師在知乎上一個回答中提到過本身創做vue的過程,最初就是嘗試實現一個相似angular1的東西,發現裏面對於數據處理很是不優雅,因而創造性的嘗試利用ES5中的Object.defineProperty來實現數據綁定,因而就有了最初的vue。vue中響應式的數據處理方式是一項頗有價值的東西。

關於響應式的實現原理,vue官網上面其實有具體介紹,下面是一張官方圖片:

vue會遍歷此data中對象全部的屬性,並使用Object.defineProperty把這些屬性所有轉爲getter/setter,而每一個組件實例都有watcher對象,它會在組件渲染的過程當中把屬性記錄爲依賴,以後當依賴項的 setter被調用時,會通知watcher從新計算,從而導致它關聯的組件得以更新。這就是響應實現的基本原理,Object.defineProperty沒法shim,因此vue不支持IE8及如下不支持ES5的瀏覽器。

一個簡單的demo:

<input type="text" id="inputName">
  <br>
  <span id="showName"></span>
// 傳統方式處理數據
    // document.getElementById('inputName').addEventListener('keyup', function (e) {
    //   document.getElementById('showName').innerText = e.target.value;
    // });

    // 利用Object.defineProperty自動響應數據
    var obj = {};
    Object.defineProperty(obj, 'name', {
      get: function () {

      },
      set: function (val) {
        document.getElementById('showName').innerText = val;
      }
    });
    document.getElementById('inputName').addEventListener('keyup', function (e) {
      obj.name = e.target.value;
    });

這個例子並非什麼複雜的實現,可是卻體現了vue最核心的東西,咱們能夠發現,Object.defineProperty下的get和set是能夠自動相應的,基於此vue實現了一套基於數據驅動視圖的自動響應系統,使得開發模型獲得了極大的簡化。


至此,本文就暫時結束了,水平通常能力有限,後面隨着理解的加深會更深刻去學習。更多文章歡迎訪問我的網站

相關文章
相關標籤/搜索