前言

一般說來,我們常將vue應用於Web業務需求,單文件組件方式和簡潔語法可以大幅提升生產效率,然而不為大多人所知的是vue的底層其實更具有吸引力,用它可以做更多高逼格的事情。

市面上有很多基於vue的core和compile做出的優化開源框架,為非Web場景引入了Vue的能力,因此學習成本低,受到廣大開發者的歡迎,下面大體列一下我所瞭解到的,有更優秀的歡迎大家評論指出

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

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

二、Vue底層架構

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

1、core

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

我將從如下幾個方面來說明core

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

1.1 掛載

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

1.2 指令

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

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實現的microtask中,亦或是MessageChannel或者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情況

  3. 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]

(1)最左邊兩個新老vnode對比,也就是oldVnode2,newVnode3,不是sameVnode;

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

(3)最左邊兩個新老vnode對比,也就是oldVnode2,newVnode3,不是sameVnode;

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

(5)最左邊兩個新老vnode對比,也就是oldVnode2,newVnode3,不是sameVnode;

(6)那最右邊兩個新老vnode對比,也就是oldVnode3newVnode3,由於key值不同,不是sameVnode

(7)當前最左邊和最右邊對比,oldVnode2,newVnode3,不是sameVnode

(8)當前最右邊和最左邊對比,oldVnode5,newVnode3,不是sameVnode

(9)在遍歷oldVnodeChildren,尋找與newVnode3為sameVnode的oldVnode,沒有找到,則用newVnode3創建一個新的元素Element6,插入到當前oldVnode2所對應元素的最左邊,dom結構發生變化

newVnodeChildren兩頭重合,退出循環,刪除剩餘未被複用元素Element2,Element3

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**

2、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>

2、如果不是第一種情況,說明是字元串,處理類似於下面的插值字元串或者純文本,同樣template去除被解析的部分,回到步驟1

{{username}}:{{password}} 或者 用戶名:密碼

3如果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("
" + _s(username) + ":" + _s(password) + "
"), _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")])])
}
}
)

3、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,這些指令依賴於具體的平臺元素。

三、應用

說了這麼多,最終目的是為了復用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綁定,不能綁定一個常量等等;

四、總結

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

這篇文章的最終目的是為了讓大家瞭解到復用Vue源碼的core和compile進行二次開發,可以做到為具體平臺帶來Vue或者類Vue的開發體驗。當然,這只是一種思路,說不定哪天,Vue風格開發體驗失寵了,那麼我們也可以把這種思路應用到新開發風格上。


推薦閱讀:
相關文章