寫文章不容易,點個讚唄兄弟 專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】vue
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧node
【Vue原理】Component - 源碼版 之 建立組件VNode ios
今天就要開啓咱們 Component 探索之旅,旅途有點長,各位請坐好,不要睡着了bash
內容的主題是,Component 的建立過程,從調用 component,到 component 掛載,到底經歷了什麼?歡迎來到 component 的心裏世界函數
建議能夠先看看白話版學習
Component 建立,我主要分了兩個流程this
一、建立 組件 VNode
二、掛載 組件 DOM
複製代碼
每一個流程涉及源碼都不少,因此每一個流程寫一篇文章。沒錯了,今天講的就是 建立組件 VNodespa
首先,咱們假定如今有這麼一個模板,使用了 test 組件prototype
而後頁面噼裏啪啦執行到了 準備掛載DOM 的步驟(以前的部分跟本主題無關,跳過)
而後頁面準備執行渲染函數 render,嗯,就是執行上面模板生成的渲染函數,以下
沒有錯,咱們的 Vue 已經走到了這一步,那麼咱們的突破口是什麼?
沒錯,就是 _c
function _createElement(
context, tag, data, children
) {
var vnode;
var options = context.$options
// 從父組件選項上拿到 對應的組件的選項
var Ctor = options.components[tag]
if (正常的HTML標籤) { ....直接新建VNode }
else if ( Ctor ) {
vnode = createComponent(
Ctor, data, context,
children, tag
);
}
return vnode
}
複製代碼
今天講的是 component,跳過其餘,直接走到 第二個 if,嗯,他調用了一個 createComponent
好的,我去前面探探路
function createComponent(
Ctor, data, context,
children, tag
) {
var baseCtor = context.$options._base;
// 建立組件構造函數
Ctor = baseCtor.extend(Ctor);
var vnode = new VNode(
"vue-component-" + (Ctor.cid) + name,
data, undefined, undefined,
undefined, context,
{
Ctor: Ctor
}
);
return vnode
}
複製代碼
這個 createComponent 什麼鬼的,做用大概是
一、建立組件構造函數
二、處理父組件給子組件的數據
三、建立組件 外殼 VNode
因爲處理數據什麼的,跟本內容無關,因此其餘源碼一概去掉,那麼就只剩下兩個流程
下面就開始這兩個流程
上面的源碼中有兩句話(以下),做用就是爲組件建立一個構造函數!
var baseCtor = context.$options._base;
Ctor = baseCtor.extend(Ctor);
複製代碼
看得懂嗎?看懂了?好吧,那我就不講了
算了,算了,仍是講吧,畢竟當時本身也是懵逼的
context 是執行整個渲染函數的上下文對象,很明顯,這裏就是頁面的 實例vm 了
$options 就是 實例自定義選項 和 全局選項合併以後的 產物
Vue.prototype._init = function(options) {
.....
vm.$options = mergeOptions( // 把兩個對象合併
vm.constructor.options,
options, vm
);
.....
}
複製代碼
沒錯,就是 Vue,你使用 new Vue 建立的頁面,構造函數確定是 Vue 啦
看下面
Vue.options = Object.create(null);
Vue.options.components = Object.create(null);
Vue.options.directives = Object.create(null);
Vue.options.filters = Object.create(null);
Vue.options._base = Vue;
複製代碼
Vue 在引入的時候,就完成了不少初始化的內容,這裏就是其中給 Vue 增長options 的部分
你看到的 component 啊,filter 什麼的啊,沒錯,保存的就是你全局註冊的 component,filter
而後每一個頁面都能使用到 全局組件,全局filter 的緣由
就是由於在 頁面實例初始化的時候,把 頁面選項 和 全局選項私下合併 了
而後,你應該能看到這一句,保存了 Vue 構造函數在 options._base 中
Vue.options._base = Vue;
複製代碼
那麼,你應該能理解前面出現的源碼了
var baseCtor = context.$options._base;
複製代碼
沒錯!baseCtor 拿到的就是 Vue!!!!
而後還有一句
Ctor = baseCtor.extend(Ctor);
複製代碼
既然 baseCtor 是 Vue,那 baseCtor.extend 是 Vue.extend?沒有錯!
正是他!完成了建立組件構造函數的偉大之舉!!讓咱們一塊兒來欣賞下
Vue.extend = function(extendOptions) {
// this 指向Vue
var Super = this;
var Sub = function VueComponent(options) {
this._init(options);
};
// 原型鏈繼承
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
// Super 永遠是 Vue,因此這裏就是 合併全局選項
// 如今 Super 就是 vue,把 Vue 和 Sub 合併
// 是把一些全局的組件 指令合併到 Sub 中
Sub.options = mergeOptions(
// optios 還包括 mixins 注入的全局
Super.options,
extendOptions
);
return Sub
};
複製代碼
這個函數,會返回一個函數 VueComponent,他就是組件的構造函數!用來下篇文章建立組件實例的!!
上面的源碼,作的事,簡單說,就是繼承父類Vue,而後合併 options 等
最後,提一下,全部實例的父類構造函數 Super 都是 Vue
並非說,組件 a 有一個子組件b,而後組件b 的父類構造函數就是 a.contructor,這是不對的,永遠是Vue,誰都是 Vue
如今就是前面代碼 createComponent 中的最後一步了
注意注意,這裏建立的是【組件外殼節點】,內部節點尚未上場,在下篇文章纔出現
至於,什麼是外殼節點,去看下個人 VNode - 源碼版
跳到相關內容看就行了
var vnode = new VNode(
"vue-component-" + Ctor.cid + name,
data, undefined, undefined,
undefined, context,
{
Ctor: Ctor
}
);
複製代碼
一、保存剛建立好的組件構造函數,下篇文章中會調用到
二、保存父組件給子組件 關聯的數據,好比 event,props 之類的(因爲跟本主題無關,爲了整潔,通通去掉了)
Component 建立 外殼節點的流程,總結以下
一、頁面渲染函數執行
二、_c('test') 執行
三、createElement 碰到 tag 是一個組件
四、從父組件中,拿到 test 組件的options,傳入 createComponent (做用是建立構造函數和 VNode)
五、createComponent 調用 Vue.extend 建立組件構造函數
六、新建 VNode,並把構造函數和父組件給子組件的數據保存進去
七、返回 VNode