這篇文章主要為大家展示了“vue中的mounted鉤子怎么用”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“vue中的mounted鉤子怎么用”這篇文章吧。
目前創(chuàng)新互聯(lián)已為上千多家的企業(yè)提供了網(wǎng)站建設、域名、網(wǎng)站空間、網(wǎng)站托管、服務器托管、企業(yè)網(wǎng)站設計、蒙城網(wǎng)站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
注:閱讀本文需要對vue的patch流程有較清晰的理解,如果不清楚patch流程,建議先了解清楚這個流程再閱讀本文,否則可能會感覺云里霧里。
聊之前我們先看一個場景
<div id="app"> <aC /> <bC /> </div>
如上所示,App.vue文件里面有兩個子組件,互為兄弟關系
組件里面自各有created和mounted兩個生命周期鉤子,a表示組件名 C是created的縮寫
// a組件 created() { console.log('aC') }, mounted() { debugger console.log('aM') }, // b組件 created() { console.log('bC') }, mounted() { debugger console.log('bM') },
請問打印順序是什么?各位讀者可以先腦補一下,后面看看對不對。
如果對vue patch流程比較熟悉的讀者,可能會認為順序是aC→aM→bC→BM,也就是a組件先創(chuàng)建,再掛載,然后到b組件重復以上流程。因為從patch的方法里面可以知道,組件created后,再走到insert進父容器的過程,是一個同步的流程,只有這個流程走完后,才會遍歷到b組件,走b組件的渲染流程。
實際上瀏覽器打印出來的順序是aC→bC→aM→bM,也就是兩個created先執(zhí)行,才到mounted執(zhí)行,和上面的分析相悖。這里先說原因,子組件從created到insert進父容器的過程還是同步的,但是insert進父容器后,也可以理解為子組件mounted,并沒有馬上調(diào)用mounted生命周期鉤子。下面從源碼角度分析一下:
先大概回顧一下子組件渲染流程,patch函數(shù)調(diào)用createElm創(chuàng)建真實element,createElm里面通過createComponent判斷當前vnode是否組件vnode,是則進入組件渲染流程
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) { var i = vnode.data; if (isDef(i)) { var isReactivated = isDef(vnode.componentInstance) && i.keepAlive; if (isDef(i = i.hook) && isDef(i = i.init)) { i(vnode, false /* hydrating */); } if (isDef(vnode.componentInstance)) { initComponent(vnode, insertedVnodeQueue); // 最終組件創(chuàng)建完后會走到這里 把組件對應的el插入到父節(jié)點 insert(parentElm, vnode.elm, refElm); if (isTrue(isReactivated)) { reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm); } return true } } }
createComponent里面就把組件對應的el插入到父節(jié)點,最后會返回到patch調(diào)用棧,調(diào)用
invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
因為子組件有vnode.parent所以會走一個分支,但是我們也看看第二個分支調(diào)用的insert是什么
function invokeInsertHook (vnode, queue, initial) { // delay insert hooks for component root nodes, invoke them after the // element is really inserted if (isTrue(initial) && isDef(vnode.parent)) { vnode.parent.data.pendingInsert = queue; } else { for (var i = 0; i < queue.length; ++i) { queue[i].data.hook.insert(queue[i]); } } }
這個insert是掛在vnode.data.hook上,在組件創(chuàng)建過程中,createComponent方法里面有一個調(diào)用
installComponentHooks,在這里把insert鉤子注入了。這個方法實際定義在componentVNodeHooks對象里面,可以看到這個insert里面調(diào)用了callHook(componentInstance, 'mounted'),這里實際上就是調(diào)用子組件的mounted生命周期。
insert: function insert (vnode) { var context = vnode.context; var componentInstance = vnode.componentInstance; if (!componentInstance._isMounted) { componentInstance._isMounted = true; callHook(componentInstance, 'mounted'); } if (vnode.data.keepAlive) { if (context._isMounted) { // vue-router#1212 // During updates, a kept-alive component's child components may // change, so directly walking the tree here may call activated hooks // on incorrect children. Instead we push them into a queue which will // be processed after the whole patch process ended. queueActivatedComponent(componentInstance); } else { activateChildComponent(componentInstance, true /* direct */); } } },
再來看看這個方法,子組件走第一個分支,僅僅執(zhí)行了一行代碼vnode.parent.data.pendingInsert = queue , 這個queue實際是在patch 開始時候,定義的insertedVnodeQueue。這里的邏輯就是把當前的insertedVnodeQueue,掛在parent的vnode data的pendingInsert上。
function invokeInsertHook (vnode, queue, initial) { // delay insert hooks for component root nodes, invoke them after the // element is really inserted if (isTrue(initial) && isDef(vnode.parent)) { vnode.parent.data.pendingInsert = queue; } else { for (var i = 0; i < queue.length; ++i) { queue[i].data.hook.insert(queue[i]); } } } // 在patch 開始時候 定義了insertedVnodeQueue為一個空數(shù)組 var insertedVnodeQueue = [];
源碼里面再搜索insertedVnodeQueue ,可以看到有這樣一段邏輯,initComponent還是在createComponent里面調(diào)用的
function initComponent (vnode, insertedVnodeQueue) { if (isDef(vnode.data.pendingInsert)) { insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert); vnode.data.pendingInsert = null; } vnode.elm = vnode.componentInstance.$el; if (isPatchable(vnode)) { // ??注意這個方法 invokeCreateHooks(vnode, insertedVnodeQueue); setScope(vnode); } else { // empty component root. // skip all element-related modules except for ref (#3455) registerRef(vnode); // make sure to invoke the insert hook insertedVnodeQueue.push(vnode); } }
insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert) 重點看這一行代碼,把 vnode.data.pendingInsert這個數(shù)組每一項push到當前vnode的insertedVnodeQueue中,注意這里是通過apply的方式,所以是把 vnode.data.pendingInsert這個數(shù)組每一項都push,而不是push pendingInsert這個列表進去。也就是說在這里,組件把他的子組件的insertedVnodeQueue里面的item收集了,因為渲染是一個深度遞歸的過程,所有最后根組件的insertedVnodeQueue能拿到所有子組件的insertedVnodeQueue里面的每一項。
從invokeInsertHook的queue[i].data.hook.insert(queue[i]) 這一行可以看出,insertedVnodeQueue里面的item應該是vnode。源碼中搜索insertedVnodeQueue.push ,可以發(fā)現(xiàn)是invokeCreateHooks這個方法把當前vnode push了進去。
function invokeCreateHooks (vnode, insertedVnodeQueue) { for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { cbs.create[i$1](emptyNode, vnode); } i = vnode.data.hook; // Reuse variable if (isDef(i)) { if (isDef(i.create)) { i.create(emptyNode, vnode); } // 把當前vnode push 到了insertedVnodeQueue if (isDef(i.insert)) { insertedVnodeQueue.push(vnode); } } }
對于組件vnode來說,這個方法還是在initComponent中調(diào)用的。
到這里就很清晰,子組件insert進父節(jié)點后,并不會馬上調(diào)用mounted鉤子,而是把組件對應到vnode插入到父vnode的insertedVnodeQueue中,層層遞歸,最終根組件拿到所有子組件的vnode,再依次循環(huán)遍歷,調(diào)用vnode的insert鉤子,從而調(diào)用了mounted鉤子。這里是先進先出的,第一個被push進去的第一個被拿出來調(diào)用,所以最深的那個子組件的mounted先執(zhí)行。最后附上一張源碼調(diào)試的圖,可以清晰的看到根組件的insertedVnodeQueue是什么內(nèi)容。
至于為什么vue要這樣設計,是因為掛載是先子后父的,子組件插入到了父節(jié)點,但是父節(jié)點還沒有真正插入到頁面中,如果這時候立馬調(diào)用子組件的mounted,對框架使用者來說可能會造成困惑,因為子組件調(diào)用mounted的時候并沒有真正渲染到頁面中,而且此時也肯定也無法通過document.querySelector的方式操作dom。
以上是“vue中的mounted鉤子怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
標題名稱:vue中的mounted鉤子怎么用
當前地址:http://vcdvsql.cn/article10/gghhgo.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、網(wǎng)站策劃、品牌網(wǎng)站建設、全網(wǎng)營銷推廣、網(wǎng)站導航、網(wǎng)站建設
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)