寫文章不容易,點個讚唄兄弟 專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】node
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧bash
【Vue原理】Component - 源碼版 之 掛載組件DOM dom
由這篇文章 從模板到DOM的簡要流程 函數
咱們知道,在生成 VNode 以後,下一步就是根據 VNode 生成DOM而後掛載了學習
在本文開始以前你能夠先看 Component - 白話版 先總體瞭解下componentui
如今開始咱們的正文this
上一篇文章 Component - 建立組件VNode ,咱們已經說到了 【頁面模板解析成 VNode 樹】的步驟spa
那今天就就到了 【頁面VNode生成DOM掛載】 了prototype
等等,今天說的不是 Component 掛載DOM 嗎?跟頁面Vnode 有什麼關係??是啊,component 的掛載確定是跟着父頁面的啊,你本身掛?自掛東南枝嗎?3d
好了,廢話不說,立刻開始
這篇 從模板到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 進行組件內部模板解析渲染,並掛載