vue源碼實現的整體流程解析


一、前言

最近一直在使用vue做項目,閑暇之余查閱了一些關於vue實現原理的資料,一方面對所了解到的知識做個總結,另外一方面希望能對看到此文章的同學有所幫助。本文如有不足之處,還請過往的大佬批評指正。

二、vue實現原理概述

vue作為一個前端漸進式的MVVM開發庫,將廣大的前端勞苦大眾從DOM操作中解放出來;說到vue的實現原理,大體可分為三個要素

1、數據的響應式,即vue可以監聽到數據的變化

2、模板引擎,模板引擎大家都應該不陌生,同之前使用的handlebars、artTemplate相似,都類似於Html語法,不過可以寫一些邏輯在上面(數據綁定和事件綁定)

3、Html的渲染,即通過模板引擎將數據渲染到頁面過程

 這三點為理論上的實現原理,不過使用vue寫項目的時候,從數據變化到頁面展示的變化大體可分為一下一個步驟

第一步:解析模板成render函數

第二步:響應式開始監聽數據的變化

第三步:首次渲染頁面,顯示頁面,並綁定數據依賴和和事件

第四步:data的變化,觸發rerender,更新視圖的顯示

一下將通過解釋vue監聽數據的變化、模板綁定數據、數據渲染至頁面、實現只渲染數據改變部分DOM這幾個部分進行講解

三、vue實現整體解析

1、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數據響應式監聽的原理,接下來我們來看一下模板引擎的實現

2、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()); }

 

 3、數據渲染至頁面

將數據渲染進頁面,通過patch函數將虛擬DOM轉換為真實DOM結構頁面進行展示,渲染頁面的函數調用如下:

初次渲染

1、執行updateComponent,執行vm._render()

2、執行render函數,會訪問到數據源

3、相應式get方法監聽到訪問的數據源執行updateComponent,到patch()方法中的 vm.$el = vm.__patch__(vm.$el, vnode);

4、patch()將虛擬DOM渲染成DOM,初次渲染完成

修改屬性渲染

1、修改屬性,被響應式set監聽到

2、set中執行updateComponent

3、updateComponent重新執行vm._render()

4、生成vnode和舊的prevVnode,通過patch進行比較  vm.$el = vm.__patch__(prevVnode, vnode);

5、DOM更新

四、小節

以上為今天所講的內容,對vue的實現原理進行了一個簡單剖析,至於vue中實現的DOM的差量更新,vue2.0之后引入的虛擬DOM,是基於vdom的diff算法原理,通過patch函數比較vdom更新前后的數據差異進行DOM的更新,篇幅有限,這里就不在對虛擬DOM進行贅述,如有興趣,歡迎閱讀我下一篇關於虛擬DOM的文章。

什么是虛擬DOM


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM