寫文章不容易,點個讚唄兄弟 專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】vue
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧node
【Vue原理】Component - 源碼版 之 建立組件VNode ios
今天就要開啓咱們 Component 探索之旅,旅途有點長,各位請坐好,不要睡着了函數
內容的主題是,Component 的建立過程,從調用 component,到 component 掛載,到底經歷了什麼?歡迎來到 component 的心裏世界學習
建議能夠先看看白話版this
Component - 白話版 prototype
Component 建立,我主要分了兩個流程3d
一、建立 組件 VNode 二、掛載 組件 DOM
每一個流程涉及源碼都不少,因此每一個流程寫一篇文章。沒錯了,今天講的就是 建立組件 VNodecode
首先,咱們假定如今有這麼一個模板,使用了 test 組件component
而後頁面噼裏啪啦執行到了 準備掛載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