寫文章不容易,點個讚唄兄弟
專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧
研究基於 Vue版本 【2.5.17】
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧node
【Vue原理】Component - 源碼版 之 掛載組件DOM dom
由這篇文章 從模板到DOM的簡要流程 函數
咱們知道,在生成 VNode 以後,下一步就是根據 VNode 生成DOM而後掛載了學習
在本文開始以前你能夠先看 Component - 白話版 先總體瞭解下componentthis
如今開始咱們的正文spa
上一篇文章 Component - 建立組件VNode ,咱們已經說到了 【頁面模板解析成 VNode 樹】的步驟prototype
那今天就就到了 【頁面VNode生成DOM掛載】 了3d
等等,今天說的不是 Component 掛載DOM 嗎?跟頁面Vnode 有什麼關係??是啊,component 的掛載確定是跟着父頁面的啊,你本身掛?自掛東南枝嗎?code
好了,廢話不說,立刻開始component
這篇 從模板到DOM的簡要流程 已經說過下面的步驟
1vm._render 執行獲得 頁面VNode
2vm._update 拿到 頁面VNode ,會開始 patch,不斷比對 【舊VNode 和 剛拿到的新VNode】
對比完以後,會調用一個 createElm 的方法去建立DOM,而後插入頁面
那如今,咱們就從 createElm 這個方法突破,前面的流程跟本內容無關,一概略過
function createElm(vnode, parentElm, refElm) { // 組件須要特殊處理 if (createComponent(vnode, parentElm, refElm)) return ...正常的標籤,須要不斷遞歸子節點調用 createElm , 而後生成DOM,並插入到父節點 }
createElm 的做用就是根據 標籤名建立 DOM 節點,而後掛載到父節點中,其中參數以下
parentElm == 父DOM 節點 refElm == 兄弟DOM節點,你插入父節點,可能也要知道插在誰附近不是嗎,不能亂插的
而後很明顯,createElm 每次掉要給你都會調用 【createComponent】 去檢測這個標籤是不是組件
若是是組件,就會去建立這個組件的實例,而且 返回 true,從而不用去執行 createElm 下面的部分
看下 createComponent
function createComponent(vnode, parentElm, refElm) { var data = vnode.data; var hook = i.hook; var init = i.init; // 調用子組件的 init 方法, init 方法就是 Vue.prototype._init if (init) { // 建立子組件的 vm 實例 init(vnode, parentElm, refElm); // 若是存在組件實例,就是上一步建立成功了 if (vnode.componentInstance) { return true } } }
有沒有好奇 vnode.data.hook.init 是什麼嗎?
他是每一個組件,都會被 【註冊進外殼節點的鉤子函數】,沒錯,就是下面的鉤子,源碼
沒錯,這就是那個鉤子的源碼
var componentVNodeHooks = { init(vnode, parentElm, refElm) { var vm= vnode.componentInstance = createComponentInstanceForVnode( vnode,activeInstance, parentElm, refElm ); // 由於 在 Vue.prototype._init 中 ,只有 $options存在 el,纔會掛載 dom // 這裏手動掛載組件 vm.$mount(vnode.elm); } ... }
那麼,鉤子是何時註冊的呢?
嗯,在上一篇文章,【建立組件外殼VNode的過程當中】,而後保存到了外殼節點的 data 上
function createComponent( Ctor, data, context, children, tag ) { ...建立組件構造函數 var hooks = data.hook || (data.hook = {}); data.hook.init = componentVNodeHooks.init ...建立組件VNode,並保存組件構造函數 和鉤子 等到 vnode 中 }
打印一下實際VNode,沒錯,有不少鉤子,可是如今只說 init
來吧,仔細看那個init 鉤子源碼,你能夠看到調用了一個方法
createComponentInstanceForVnode
開始深刻探索它.........
createComponentInstanceForVnode 函數做用就是給 component 【增長定製options】 + 【調用組件構造函數】
function createComponentInstanceForVnode( vnode, parent, parentElm, refElm ) { // 增長 component 特有options var options = { _isComponent: true, parent: parent, // 父實例 _parentVnode: vnode, // 外殼節點 _parentElm: parentElm , // 父DOM _refElm: refElm // 兄弟DOM }; // vnode.components.Ctor 就是 構造函數 ,裏面會調用 Vue.prototype._init return new vnode.componentOptions.Ctor(options) }
vnode.componentOptions.Ctor 就是 構造函數,就是下面這個,上篇文章 Component - 建立組件VNode 時保存在外殼節點的
function VueComponent(options) { this._init(options); }
new 了以後,天然而然,走到了 _init 方法,在 init 方法中,有一個特殊照顧 component 的方法,專門給 component 實例設置options
"這一步跟 掛載組件DOM 沒什麼關聯,想去掉的,可是想一想仍是先保留下來,完整整個流程"
Vue.prototype._init = function(options) { if (若是是組件) { initInternalComponent(vm, options); } }
組件初始化 initInteralComponent
function initInternalComponent(vm, options) { // 這個options 就是在建立構造函數時,合併的 options,全局選項和組件設置選項 var opts = vm.$options = Object.create(vm.constructor.options); // 保存父節點,外殼節點,兄弟節點等 var parentVnode = options._parentVnode; // _parentVnode 是外殼節點 opts.parent = options.parent; // options.parent 是 父實例 opts._parentVnode = parentVnode; opts._parentElm = options._parentElm; opts._refElm = options._refElm; // 保存父組件給子組件關聯的數據 var vnodeComponentOptions = parentVnode.componentOptions; opts.propsData = vnodeComponentOptions.propsData; opts._parentListeners = vnodeComponentOptions.listeners; opts._renderChildren = vnodeComponentOptions.children; opts._componentTag = vnodeComponentOptions.tag; // 保存渲染函數 if (options.render) { opts.render = options.render; opts.staticRenderFns = options.staticRenderFns; } }
這個時候, init 的過程就完成了
下一步就是到了 mount 過程
能夠再回看下 「componentVNodeHooks.init 」 那個鉤子源碼
在建立組件實例成功以後,會手動調用實例 vm.$mount 進行掛載,就是這句代碼完成的功能
然而,掛載的步驟,就是正常標籤掛載的步驟了
詳情能夠查看 從模板到DOM的簡要流程
的 mount 過程,是一毛同樣的,就很少說了
一、父頁面已經拿到了 VNode,其中會調用 createElm 根據 VNode 生成DOM,進行掛載
二、不斷的遞歸遍歷子節點,使用 createComponent 判斷標籤是不是組件
三、遇到組件,拿到組件外殼VNode 的data(data 保存有父組件給子組件的,事件,props,構造函數,鉤子)
四、從 data 中拿到 hook,hook 中拿到 init 鉤子,並執行 init 鉤子
五、init 鉤子中,調用 createComponentInstanceForVnode 調用組件構造函數,並返回組件
六、init 鉤子中,使用上一步返回的實例,手動調用 vm.$mount 進行組件內部模板解析渲染,並掛載