1、前言html
最近一直在使用vue作項目,閒暇之餘查閱了一些關於vue實現原理的資料,一方面對所瞭解到的知識作個總結,另一方面但願能對看到此文章的同窗有所幫助。本文若有不足之處,還請過往的大佬批評指正。前端
2、vue實現原理概述vue
vue做爲一個前端漸進式的MVVM開發庫,將廣大的前端勞苦大衆從DOM操做中解放出來;說到vue的實現原理,大致可分爲三個要素:node
一、數據的響應式,即vue能夠監聽到數據的變化算法
二、模板引擎,模板引擎你們都應該不陌生,同以前使用的handlebars、artTemplate類似,都相似於Html語法,不過能夠寫一些邏輯在上面(數據綁定和事件綁定)瀏覽器
三、Html的渲染,即經過模板引擎將數據渲染到頁面過程閉包
這三點爲理論上的實現原理,不過使用vue寫項目的時候,從數據變化到頁面展現的變化大致可分爲一下一個步驟:dom
第一步:解析模板成render函數函數
第二步:響應式開始監聽數據的變化this
第三步:首次渲染頁面,顯示頁面,並綁定數據依賴和和事件
第四步:data的變化,觸發rerender,更新視圖的顯示
一下將經過解釋vue監聽數據的變化、模板綁定數據、數據渲染至頁面、實現只渲染數據改變部分DOM這幾個部分進行講解
3、vue實現總體解析
一、vue如何實現對數據的監聽的?
說到vue對數據的監聽,不得不提到Object.defineProperty,當前主流瀏覽器均支持此屬性,vue的數據雙向綁定及數據的監聽都是基於此實現的;請看下面代碼:
1 var obj = {};
2 var name = "MrGao"
3 Object.defineProperty(obj, "name", { 4 get: function() { // 獲取屬性值 5 return name; // name的初始值爲"MrGao" 6 }, 7 set: function(newval) { // set新的值給屬性 8 name = newval 9 } 10 }) 11 console.log(obj.name) // MrGao 12 obj.name = "MrBone"; 13 console.log(obj.name) // MrBone
能夠看到Object.defineProperty傳了三個參數進去
第一個參數爲目標對象
第二個參數爲要定義的屬性或方法名稱,(若是對象中不包含此屬性將此屬性添加到目標對象裏,vue中將data數據指向vm就是用的這裏,下面將詳細講解)
第三個參數爲目標屬性的所擁有的特性
這三個參數爲必填參數,另外對於第三個參數除get和set以外還有一些其它的屬性,在這裏提一下
value: 屬性的值
writable:屬性的值是否能被重寫,當設置爲false的時候爲只讀,不能經過set進行從新賦值
configurable:是否能夠設置他的其餘屬性(value,writable)
enumerable:是否能夠在Object.keys或for...in中列舉出來
get和set上面例子已經提到
Object.defineProperty在vue中的實現
咱們知道在寫vue的時候都是能夠經過this.*來讀取改變在data中定義的屬性,那麼這是怎麼實現的呢?這就是經過Object.defineProperty將data中的屬性指向到vm中,即this就是之vm實例,參考一下實現代碼:
1 var data = {
2 name: "MrGao", 3 age: 22, 4 address: "HangZhou" 5 }; 6 var vm = {}; 7 8 for(key in data) { 9 (function(key){ // 採用閉包,保證key的獨立做用域 10 Object.defineProperty(vm, key, { 11 get: function() { 12 return data[key] 13 }, 14 set: function(newval) { 15 data[key] = newval 16 } 17 }) 18 })(key) 19 } 20 21 console.log(vm.name); 22 vm.name = "MrBone"; 23 console.log(vm.name);
經過分析上面代碼能夠看出vue經過Object.defineProperty將data中的屬性指向vm實例,即this能夠獲取data中的屬性
以上爲vue數據響應式監聽的原理,接下來咱們來看一下模板引擎的實現
二、vue模板引擎
說模板以前先來看下模板是什麼:
本質:字符串
帶邏輯: 如v-if、v-for、v-model
特色:最終要經過js轉換成html代碼來顯示在頁面上
因此若是要讓有邏輯的模板顯示在頁面上,就須要經過js聲明一個函數來處理這件事情,咱們給它起個名字叫作render
經過都vue源碼,可獲取關於render函數中的一些核心函數,下面經過一個簡單的例子來作下介紹:
經過以上代碼可看出vue中render函數有兩個特色,一是使用了with函數,二是模板中全部的信息都包含在render函數中;其中函數中的this指的就是vm,因此_c、_v、_s、tittle分別指的是vm._c、vm._v、vm._s、vm.tittle。這裏重點說下_c,這裏的_c和snabbdom.js中的h()函數類似,下面還會說到vdom中的patch()函數,要了解虛擬DOM相關細節我將在下一篇博客中介紹。
此處render函數將返回一個虛擬DOM,即vnode,並將vnode傳到patch()函數中;參考下面代碼:
vm._updata(vnode) {
const prevVnode = vm._vnode; vm._vnode = vnode; // 將新的vnode賦值給舊的_vnode if (!prevVnode) { // 若是舊的_vnode不存在則將dom掛載在vm.$el vm.$el = vm.__patch__(vm.$el, vnode); } else { vm.$el = vm.__patch__(prevVnode, vnode); } } function updateComponent() { // vm._render爲返回的虛擬DOM vm._update(vm._render()); }
三、數據渲染至頁面
將數據渲染進頁面,經過patch函數將虛擬DOM轉換爲真實DOM結構頁面進行展現,渲染頁面的函數調用以下:
初次渲染
一、執行updateComponent,執行vm._render()
二、執行render函數,會訪問到數據源
三、相應式get方法監聽到訪問的數據源執行updateComponent,到patch()方法中的 vm.$el = vm.__patch__(vm.$el, vnode);
四、patch()將虛擬DOM渲染成DOM,初次渲染完成
修改屬性渲染
一、修改屬性,被響應式set監聽到
二、set中執行updateComponent
三、updateComponent從新執行vm._render()
四、生成vnode和舊的prevVnode,經過patch進行比較 vm.$el = vm.__patch__(prevVnode, vnode);
五、DOM更新
4、小節
以上爲今天所講的內容,對vue的實現原理進行了一個簡單剖析,至於vue中實現的DOM的差量更新,vue2.0以後引入的虛擬DOM,是基於vdom的diff算法原理,經過patch函數比較vdom更新先後的數據差別進行DOM的更新,篇幅有限,這裏就不在對虛擬DOM進行贅述,若有興趣,歡迎閱讀我下一篇關於虛擬DOM的文章。