Vue底層架構及其應用

閱讀時間大約16~22min
做者:汪汪
我的主頁:www.zhihu.com/people/wang…javascript

1、前言

市面上有不少基於vue的core和compile作出的優化開源框架,爲非Web場景引入了Vue的能力,所以學習成本低,受到廣大開發者的歡迎,下面大致列一下我所瞭解到的,有更優秀的歡迎你們評論指出html

分類 技術
跨平臺native weex
小程序 mpvue
服務端渲染 Vue SSR
小程序多端統一框架 uni-app

至於提供類Vue開發體驗的框架就數不勝數了,如小程序框架--wepy,前端

從其餘的方面看,github日榜,Vue天天都有過100的star,足見其火熱程度,這也是爲何你們都爭先恐後的在非web領域提供Vue的支持。那麼Vue的底層架構及其應用就尤其重要了vue

2、Vue底層架構

瞭解Vue的底層架構,是爲非web領域提供Vue能力的大前提。Vue核心分爲三大塊:core,compiler,platform,下面分別介紹其原理及帶來的能力。java

一、core

core是Vue的靈魂所在,正是core實現了經過vnode方式,遞歸生成指定平臺視圖並在數據變更時,自動diff更新視圖,也正是由於VNode機制,使得core是平臺無關的,就算core的功能在於UI渲染。node

我將從以下幾個方面來講明coregit

  • 掛載
  • 指令
  • Vnode----劃重點
  • 組件實例vm及vm間的關係
  • nextTick
  • Watcher----劃重點
  • vnode diff算法----劃重點
  • core總結

1.1 掛載

將vnode生成的具體平臺元素append到已知節點上。咱們拿web平臺舉例,用vnode經過document.createElement生成dom,而後在append到文檔樹中某個節點上。後面咱們也會常常說到掛載組件,它指的就是執行組件對應render生成vnode,而後遍歷vnode生成具體平臺元素,組件的根節點元素會被append到父元素上。github

1.2 指令

指令在Vue中是具備特定含義的屬性,指令分兩類,一類是編譯時處理,在生成的render函數上體現,如:v-if,v-for,另一類是運行時使用,更多的是對生成的具體平臺元素操做,web平臺的話就是對dom的操做web

1.3 VNode---------劃重點

vnode是虛擬node節點,是具體平臺元素對象的進一步抽象(簡化版),每個平臺元素對應一個vnode,可經過vnode結構完整還原具體平臺元素結構。 下面以web平臺來解釋vnode。對於web,假定有以下結構:算法

<div class="box" @click="onClick">------------------對應一個vnode
    <p class="content">哈哈</p>-------對應一個vnode
    <TestComps></TestComps>----------自定義組件一樣對應一個vnode
    <div></div>-----------------------對應一個vnode
</div>
複製代碼

通過Vue的compile模塊將生成渲染函數,執行這個渲染函數就會生成對應的vnode結構:

//這裏我只列出關鍵的vnode信息
{
  tag:'div',
  data:{attr:{},staticClass:'box',on:{click:onClick}},
  children:[{
    tag:'p',
    data:{attr:{},staticClass:'content',on:{}},
    children:[{
      tag:'',
      data:{},
      text:'哈哈'
    }]
  },{
    tag:'div',
    data:{attr:{},on:{}},
  },{
    tag:'TestComps',
    data:{
        attr:{},
        hook:{
            init:fn,           
            prepatch:fn,
            insert:fn,
           destroy:fn
        }
     },
  }]  
}
複製代碼

最外層的div對應一個vnode,包含三個孩子vnode,注意自定義組件也對應一個vnode,不過這個vnode上掛着組件實例

1.4 組件實例vm及vm間的關係---------劃重點

組件實例其實就是Vue實例對象,只有自定義組件纔會有,平臺相關元素是沒有的,要看懂Vue的core,明白下面這個關係很重要。如今,讓咱們來直觀感覺下:

假定有以下結構的模板,元素上的vnode表示生成的對應vnode名稱:

// new Vue的template,對應的實例記爲vm1
<div vnode1>
  <p vnode2></p>
  <TestComps vnode3
             testAttr="hahha"
             @click="clicked"
             :username="username"
             :password="password"></TestComps>
</div>
複製代碼
// TestComps的template,對應的實例記爲vm2
<div vnode4>
  <span vnode5></span>
  <p vnode6></p>
</div>
複製代碼
// 生成的vnode關係樹爲
vnode1={
  tag:'div',
  children:[vnode2,vnode3]
}
vnode3={
  tag:'TestComps',
  children:undefined,
  parent:undefined
}
vnode4={
  tag:'div',
  children:[vnode5,vnode6],
  parent:vnode3             //這一點關係很重要
}
複製代碼
// 生成的vm關係樹爲
vm1={
  $data:{password: "123456",username: "aliarmo"}, //組件對應state
  $props:{} //使用組件時候傳下來到模板裏面的數據
  $attrs:{},
  $children:[vm2],             
  $listeners:{}
  $options: {
    components: {}
    parent: undefined   //父組件實例
    propsData: undefined    //使用組件時候傳下來到模板裏面的數據
    _parentVnode: undefined 
  }
  $parent:undefiend               //當前組件的父組件實例
  $refs:{}                 //當前組件裏面包含的dom引用
  $root:vm1                 //根組件實例
  $vnode:undefined                 //組件被引用時候的那個vnode,好比<TestComps></TestComps>
  _vnode:vnode1       //當前組件模板根元素所對應的vnode對象
}

vm2={
  $data:{} //組件對應state
	$props:{password: "123456",username: "aliarmo"} //使用組件時候傳下來到模板裏面的數據
  $attrs:{testAttr:'hahha'},
  $children:[],             
  $listeners:{click:fn}
  $options: {
    components: {}
    parent: vm1   //父組件實例
    propsData: {password: "123456",username: "aliarmo"}    //使用組件時候傳下來到模板裏面的數據
    _parentVnode: vnode3 
  }
  $parent:vm1               //當前組件的父組件實例
  $refs:{}                 //當前組件裏面包含的dom引用
  $root:vm1                 //根組件實例
  $vnode:vnode3                 //組件被引用時候的那個vnode,好比<TestComps></TestComps>
  _vnode:vnode4       //當前組件模板根元素所對應的vnode對象
}
複製代碼

1.5 nextTick

它可讓咱們在下一個事件循環作一些操做,而非在本次循環,用於異步更新,原理在於microtask和macrotask

讓咱們來看段代碼:

new Promise(resolve=>{
  return 123
}).then(data=>{
  console.log('step2',data)
})
console.log('step1')
複製代碼

結果是先輸出 step1,而後在step2,resolve的promise是一個microtask,同步代碼是macrotask

// 在Vue中
this.username='aliarmo' // 能夠觸發更新
this.pwd='123'          // 一樣能夠觸發更新
複製代碼

那同時改變兩個state,是否會觸發兩次更新呢,並不會,由於this.username觸發更新的回調會被放入一個經過Promise或者MessageChannel實現的microtask中,亦或是setTimeout實現的macrotask,總之到了下一個事件循環。

1.6 Watcher---------劃重點

一個組件對應一個watcher,在掛載組件的時候建立這個觀察者,組件的state,包含data,props都是被觀察者,被觀察者的任何變化會被通知到觀察者,被觀察者的變更致使觀察者執行的動做是vm._update(vm._render(), hydrating),組件從新render生成vnode並patch。

明白這個關係很重要:觀察者包含對變更作出響應的定義,一個組件對應一個觀察者對應組件裏面的全部被觀察者,被觀察者可能被用於其餘組件,那麼一個被觀察者會對應多個觀察者,當被觀察者發生變更時,通知到全部觀察者作出更新響應。

組件A的state1發生了變化,那會致使觀察了這個state1的watcher收到變更通知,會致使組件A從新渲染生成新的vnode,在組件A新vnode和老的vnode patch的過程當中,會updateChildrenComponent,也就是致使子組件B的props被從新設置一個新值,由於子組件B是有觀察傳入的state1的,所以會通知到相應watcher,致使子組件B的更新

整個watcher體系的創建過程

  1. 建立組件實例的時候會對data和props進行observer,
  2. 對傳入的props進行淺遍歷,從新設定屬性的屬性描述符get和set,若是props的某個屬性值爲對象,那麼這個對象在父組件是被深度observe過的,因此props是淺遍歷
  3. observer會深度遍歷data,對data所包含屬性從新定義,即defineReactive,從新設定屬性描述符的get和set
  4. 在mountComponent的時候,會new Wacther,當前watcher實例會被pushTarget,設定爲目標watcher,而後執行vm._update(vm._render(), hydrating),執行render函數致使屬性的get函數被調用,每一個屬性會對應一個dep實例,在這個時候,dep實例關聯到組件對應的watcher,實現依賴收集,關聯後popTarget。
  5. 若是有子組件,會致使子組件的實例化,從新執行上述步驟

state變更響應過程

  1. 當state變更後,調用屬性描述符的set函數,dep會通知到關聯的watcher進入到nextTick任務裏面,這個watcher實例的run函數包含vm._update(vm._render(), hydrating),執行這個run函數,致使從新生成vnode,進行patch,通過diff,達到更新UI目的

父組件state變化如何致使子組件也發生變化?

父組件state更新後,會致使渲染函數從新執行,生成新的vnode,在oldVnode和newVnode patch的過程當中,若是遇到的是組件vnode,會updateChildrenComponent,這裏面作的操做就是更新子組件的props,由於子組件是有監聽props屬性的變更的,致使子組件re-render

父組件傳入一個對象給子組件,子組件改變傳入的對象props,父組件又是如何被更新到的?

大前提:若是父組件傳給子組件的props中有對象,那麼子組件接收到的是這個對象的引用。也就是ParentComps中的this.person和SubComps中的this.person指向同一個對象

// 假定父組件傳person對象給子組件SubComps
Vue.component('ParentComps',{
  data(){
    return {
      person:{
        username:'aliarmo',
        pwd:123
      }
    }
  },
  template:`
    <div>
      <p>{{person.username}}</p>
      <SubComps :person="person" />
    </div>
  `
})
複製代碼

如今咱們在SubComps裏面,更新person對象的某個屬性,如:this.person.username='wmy' 這樣會致使ParentComps和SubComps的更新,爲何呢?

由於Vue在ParentComps中會深度遞歸觀察對象的每一個屬性,在第一次執行ParentComps的render的時候,綁定ParentComps的Watcher,傳入到SubComps後,不會對傳入的對象在進行觀察,在第一次執行SubComps的render的時候,會綁定到SubComps的Watcher,所以當SubComps改變了this.person.username的值,會通知到兩個Watcher,致使更新。這很好的解釋了憑空在傳入的props屬性對象上掛載新的屬性不觸發渲染,由於傳入的props屬性對象是在父組件被觀察的。

1.7 vnode diff算法---------劃重點

當組件的state發生變化,從新執行渲染函數生成新的vnode,而後將新生成的vnode與老的vnode進行對比,以最小的代價更新原有視圖。diff算法的原理是經過移動、新增、刪除和替換oldChildrenVnodes對應的結構來生成newChildrenVnodes對應的結構,而且每一個老的元素只能被複用一次,老元素最終的位置取決於當前新的vnode。要明確傳入diff算法的是兩個sameVnode的孩子節點,從二者的開頭和結尾位置,同時往中間靠,直到二者中的一個到達中間。

PS:oldChildrenVnodes表示老的孩子vnode節點集合,newChildrenVnodes表示state變化後生成的新的孩子vnode節點集合

說這個算法以前,先得明白如何判斷兩個vnode爲sameVnode,我只大致列一下:

  1. vnode的key值相等,例如<Comps1 key="key1" />, <Comps2 key="key2" />,key值就不相等, <Comps1 key="key1" />, <Comps2 key="key1" />, key值就是相等的, <div></div>,<p></p>,這兩個的key值是undefined,key值相等,這個是sameVnode的大前提。
  2. vnode的tag相同,都是註釋或者都不是註釋,同時定義或未定義data,標籤爲input則type必須相同,還有些其餘的條件跟咱們不太相關就不列出來了。

整個vnode diff流程

大前提,要看懂這個vnode diff,務必先明白vnode是啥,如何生成的,vnode與elm的關係,詳情請看上面的vnode概念

  1. 若是兩個vnode是sameVnode,則進行patch vnode
  2. patch vnode過程

 (1)首先vnode的elm指向oldVnode的elm

 (2)使用vnode的數據更新elm的attr,class,style,domProps,events等

 (3)若是vnode是文本節點,則直接設置elm的text,結束

 (4)若是vnode是非文本節點&&有孩子&&oldVnode沒有孩子,則elm直接append

 (5)若是vnode是非文本節點&&沒有孩子&&oldVnode有孩子,則直接移除elm的孩子節點

 (6)若是非文本節點&&都有孩子節點,則updateChildren,進入diff 算法,前面5個步驟排除了不能進行diff狀況

  1. diff 算法,這裏以web平臺爲例

這裏還有強調下,傳入diff算法的是兩個sameVnode的孩子節點,那麼如何用newChildrenVnodes替換oldChildrenVnodes,最簡單的方式莫過於,遍歷newChildrenVnodes,直接從新生成這個html片斷,皆大歡喜。可是這樣作會 不斷的createElement,對性能有影響,因而前輩們就想出了這個diff算法。

(1)取二者最左邊的節點,判斷是否爲sameVnode,若是是則進行上述的第二步patch vnode過程,整個流程走完後,此時elm的class,style,events等已經更新了,elm的children結構也經過前面說的整個流程獲得了更新,這時候就看是否須要移動這個elm了,由於都是孩子的最左邊節點,所以位置不變,最左邊節點位置向前移動一步

(2)若是不是(1)所述case,取二者最右邊的節點,跟(1)的斷定流程同樣,不過是最右邊節點位置向前移動一步

(3)若是不是(1)(2)所述case,取oldChildrenVnodes最左邊節點和newChildrenVnodes最右邊節點,跟(1)的斷定流程同樣,不過,elm的位置須要移動到oldVnode最右邊elm的右邊,由於vnode取的是最右邊節點,若是與oldVnode的最右邊節點是sameVnode的話,位置是不用改變的,所以newChildrenVnodes的最右節點和oldChildrenVnodes的最右節點位置是對應的,但因爲是複用的oldChildrenVnodes的最左邊節點,oldChildrenVnodes最右邊節點尚未被複用,所以不能替換掉,因此移動到oldChildrenVnodes最右邊elm的右邊。而後oldChildrenVnodes最左邊節點位置向前移動一步,newChildrenVnodes最右邊節點位置向前移動一步

(4)若是不是(1)(2)(3)所述case,取oldChildrenVnodes最右邊節點和newChildrenVnodes最左邊節點,跟(1)的斷定流程同樣,不過,elm的位置須要移動到oldChildrenVnodes最左邊elm的左邊,由於vnode取的是最左邊節點,若是與oldChildrenVnodes的最左邊節點是sameVnode的話,位置是不用改變的,所以newChildrenVnodes的最左節點和oldChildrenVnodes的最左節點位置是對應的,但因爲是複用的oldChildrenVnodes的最右邊節點,oldChildrenVnodes最左邊節點尚未被複用,所以不能替換掉,因此移動到oldChildrenVnodes最左邊elm的左邊。而後oldChildrenVnodes最右邊節點位置向前移動一步,newChildrenVnodes最左邊節點位置向前移動一步

(5)若是不是(1)(2)(3)(4)所述case,在oldChildrenVnodes中尋找與newChildrenVnodes最左邊節點是sameVnode的oldVnode,若是沒有找到,則用這個新的vnode建立一個新element,插入位置如後所述,若是找到了,則跟(1)的斷定流程同樣,不過插入的位置是oldChildrenVnodes的最左邊節點的左邊,由於若是newChildrenVnodes最左邊節點與oldChildrenVnodes最左邊節點是sameVnode的話,位置是不用變的,而且複用的是oldChildrenVnodes中找到的oldVNode的elm。被複用過的oldVnode後面不會再被取出來。而後newChildrenVnodes最左邊節點位置向前移動一步

(6)通過上述步驟,oldChildrenVnodes或者newChildrenVnodes的最左節點與最右節點重合,退出循壞

(7)若是是oldChildrenVnodes的最左節點與最右節點先重合,說明newChildrenVNodes還有節點沒有被插入,遞歸建立這些節點對應元素,而後插入到oldChildrenVnodes的最左節點的右邊或者最右節點的左邊,由於是從二者的開始和結束位置向中間靠攏,想一想,若是newChildrenVNodes剩餘的第一個節點與oldChildrenVnodes的最左邊節點爲sameVnode的話,位置是不用變的

(8)若是是newChildrenVnodes的最左節點與最右節點先重合,說明oldChildrenVnodes中有一段結構沒有被複用,開始和結束位置向中間靠攏,所以沒有被複用的位置是oldChildrenVnodes的最左邊和最右邊之間節點,刪除節點對應的elm便可。

舉個栗子來描述下具體的diff過程(web平臺):

// 有Vue模板以下
<div>    ------ oldVnode1,newVnode1,element1
  <span v-if="isShow1"></span>  -------oldVnode2,newVnode2,element2
  <div :key="key"></div>  -------oldVnode3,newVnode3,element3
  <p></p>    -------oldVnode4,newVnode4,element4
  <div v-if="isShow2"></div>    -------oldVnode5,newVnode5,element5
</div>

// 若是 isShow1=true,isShow2=true,key="aliarmo"那麼模板將會渲染成以下:
<div>
  <span></span>--------------element2
  <div key="aliarmo"></div>----------element3
  <p></p>-------------element4
  <div></div>----------element5
</div>

// 改變state,isShow1=false,isShow2=true,key="wmy",那麼模板將會渲染成以下:
<div>
  <div key="wmy"></div>------------element6
  <p></p>-------------------element4
  <div></div>---------element5
</div>
複製代碼

那麼,改變state後的dom結構是如何生成的?

如上圖,在isShow1=true,isShow2=true,key="aliarmo"條件下,生成的vnode結構是: oldVnode1,oldVnodeChildren=[oldVnode2,oldVnode3,oldVnode4,oldVnode5]

對應的dom結構爲:

改變state爲isShow1=false,isShow2=true,key="wmy"後,生成的新vnode結構是

newVnode1,newVnodeChildren=[newVnode3,newVnode4,newVnode5]

最左邊兩個新老vnode對比,也就是oldVnode2newVnode3,不是sameVnode

那最右邊兩個新老vnode對比,也就是oldVnode5newVnode5,是sameVnode,不用移動原來的Element5所在位置,原有dom結構未發生變化,

最左邊兩個新老vnode對比,也就是oldVnode2newVnode3,不是sameVnode

那最右邊兩個新老vnode對比,也就是oldVnode4newVnode4,是sameVnode,不用移動原來的Element4所在位置,原有dom結構未發生變化,

最左邊兩個新老vnode對比,也就是oldVnode2newVnode3,不是sameVnode

那最右邊兩個新老vnode對比,也就是oldVnode3newVnode3,因爲key值不一樣,不是sameVnode

當前最左邊和最右邊對比,oldVnode2newVnode3,不是sameVnode

當前最右邊和最左邊對比,oldVnode5newVnode3,不是sameVnode

在遍歷oldVnodeChildren,尋找與newVnode3sameVnodeoldVnode,沒有找到,則用newVnode3建立一個新的元素Element6,插入到當前oldVnode2所對應元素的最左邊,dom結構發生變化 newVnodeChildren兩頭重合,退出循環,刪除剩餘未被複用元素Element2Element3

1.8 core總結

如今咱們終於能夠理一下,從new Vue()開始,core裏面發生了些什麼

  1. new Vue()或者new自定義組件構造函數(繼承自Vue)
  2. 初始化,props,methods,computed,data,watch,並給state加上Observe,調用生命週期created
  3. 開始mount組件,mount以前確保render函數的生成
  4. new Watcher,致使render和patch,注意一個watcher對應一個組件,watcher對變化的響應是從新執行render生成vnode進行patch
  5. render在當前組件上下文(組件實例)執行,生成對應的vnode結構
  6. 若是沒有oldVnode,那patch就是深度遍歷vnode,生成具體的平臺元素,給具體的平臺元素添加屬性和綁定事件,調用自定義指令提供的鉤子函數,並append到已存在的元素上,在遍歷的過程當中,若是遇到的是自定義組件,則從步驟1開始重複
  7. 若是有oldVnode,那patch就是利用vnode diff算法在原有的平臺元素上進行修修補補,不到萬不得已不建立新的平臺元素
  8. state發生變化,通知到state所在組件對應的watcher,從新執行render生成vnode進行patch,也就是回到步驟4

二、compiler

Vue的compiler部分負責對template的編譯,生成render和staticRender函數,編譯一次永久使用,因此通常咱們在構建的時候就作了這件事情,以提升頁面性能。執行render和staticRender函數能夠生成VNode,從而爲core提供這一層抽象。

template ==》 AST ==》 遞歸ATS生成render和staticRender ==》VNode

(1)template轉化成AST過程

先讓咱們來直觀感覺下AST,它描述了下面的template結構

// Vue模板

let template = `

 <div class="Test" :class="classObj" v-show="isShow">

 {{username}}:{{password}}

 <div>

 <span>hahhahahha</span>

 </div>

 <div v-if="isVisiable" @click="onClick"></div>
 <div v-for="item in items">{{item}}</div>


 </div>

 `
複製代碼

下面描述下template轉爲AST的簡要過程:

  1. 若是template是以<開始的字符串,則判斷是評論,仍是Doctype仍是結束標籤,或者是開始標籤,這裏只說處理開始和結束標籤。

(1)若是是開始標籤,則處理相似於下面字符串

<div class="Test" :class="classObj" v-show="isShow">
複製代碼

經過正則能夠很容易解析出tag,全部屬性列表,再對屬性列表進行分類,分別解析出v-if,v-for等指令,事件,特殊屬性等,template去除被解析的部分,回到步驟1

(2)若是是結束標籤,則處理相似於下面字符串,一樣template去除被解析的部分,回到步驟1

</div>
複製代碼
  1. 若是不是第一種狀況,說明是字符串,處理相似於下面的插值字符串或者純文本,一樣template去除被解析的部分,回到步驟1
{{username}}:{{password}} 或者 用戶名:密碼
複製代碼
  1. 若是template爲空,解析結束

(2)AST生成render和staticRender

主要是遍歷ast(有興趣的同窗能夠本身體驗下,如:遍歷AST生成還原上述模板,相信會有不同的體驗),根據每一個節點的屬性拼接渲染函數的字符串,如:模板中有v-if="isVisiable",那麼AST中這個節點就會有一個if屬性,這樣,在建立這個節點對應的VNode的時候,就會有

(isVisiable) ? _c('div') : _e()
複製代碼

在with的做用下,isVisiable 的值決定了VNode是否生成。固然,對於一些指令,在編譯時是處理不了的,會在生成VNode的時候掛載在VNode上,解析VNode時再進行進一步處理,好比v-show,v-on。

下面是上面模板生成的render和staticRender:

// render函數
(function anonymous() {
    with (this) {
        return _c('div', {
            directives: [{
                name: "show",
                rawName: "v-show",
                value: (isShow),
                expression: "isShow"
            }],
            staticClass: "Test",
            class: classObj
        }, [_v("\n " + _s(username) + ":" + _s(password) + "\n "), _m(0), _v(" "), (isVisiable) ? _c('div', {
            on: {
                "click": onClick
            }
        }) : _e(), _v(" "), _l((items), function(item) {
            return _c('div', [_v(_s(item))])
        })], 2)
    }
}
)
// staticRender
(function anonymous() {
 with (this) {
  return _c('div', [_c('span', [_v("hahhahahha")])])
 } 
}
)
複製代碼

其中this是組件實例,_c、_v分別用於建立VNode和字符串,像username和password是在定義組件時候傳入的state並被掛載在this上。

三、platform

platform模塊與具體平臺相關,咱們能夠在這裏定義平臺相關接口傳入runtime和compile,以實現具體平臺的定製化,所以爲其餘平臺帶來Vue能力,大部分工做在這裏。

須要傳入runtime的是如何建立具體的平臺元素,平臺元素之間的關係以及如何append,insert,remove平臺元素等,元素生成後須要進行的屬性,事件監聽等。拿web平臺舉例,咱們須要傳入document.createElement,document.createTextNode,遍歷vnode的時候生成HTML元素;掛載時須要的insertBefore;state發生變化致使vnode diff時的remove,append等。還有生成HTML元素後,用setAttribute和removeAttribute操做屬性;addEventListener和removeEventListener進行事件監聽;提供一些有利於web平臺使用的自定義組件和指令等等

須要傳入compile的是對某些特殊屬性或者指令在編譯時的處理。如web平臺,須要對class,style,model的特殊處理,以區別於通常的HTML屬性;提供web平臺專用指令,v-html(編譯後實際上是綁定元素的innerHTML),v-text(編譯後實際上是綁定元素的textContent),v-model,這些指令依賴於具體的平臺元素。

3、應用

說了這麼多,最終目的是爲了複用Vue的core和compile,以期在其餘的平臺上帶來Vue或者類Vue的開發體驗,前面也說了不少複用成功的例子,若是你要爲某一平臺帶來Vue的開發體驗,能夠進行參考。大前端概念下,指不定那天,汽車顯示屏,智能手錶等終端界面就能夠用Vue來進行開發,爽歪歪。那麼,如何複用呢?固然我只說必選的,你能夠定製更多更復雜的功能方便具體平臺使用。

  1. 定義vnode生成具體平臺元素所須要的nodeOps,也就是元素的增刪改查,對於web來講nodeOps是要建立,移動真正的dom對象,若是是其餘平臺,可自行定義這些元素的操做方法;

  2. 定義vnode生成具體平臺元素所須要的modules,對於web來講,modules是用來操做dom屬性的方法;

  3. 具體平臺須要定義一個本身的$mount方法給Vue,掛載組件到已存在的元素上;

  4. 還有一些方法,如isReservedTag,是否爲保留的標籤名,自定義組件名稱不能與這些相同;mustUseProp,斷定元素的某個屬性必需要跟組件state綁定,不能綁定一個常量等等;

4、總結

軟件行業有一句名言,沒有什麼問題是添加一層抽象層不能解決的,若是有那就兩層,Vue的設計正是如此(可能Vue也是借鑑別人的),compile的AST抽象層銜接了模板語法和render函數,通過VNode這個抽象層讓core剝離了具體平臺。

這篇文章的最終目的是爲了讓你們瞭解到複用Vue源碼的core和compile進行二次開發,能夠作到爲具體平臺帶來Vue或者類Vue的開發體驗。固然,這只是一種思路,說不定哪天,Vue風格開發體驗失寵了,那麼咱們也能夠把這種思路應用到新開發風格上。


關注【IVWEB社區】公衆號獲取每週最新文章,通往人生之巔!

相關文章
相關標籤/搜索