路由場景下父子組件的生命週期順序來個刨根問底

<p>你們中秋假期快樂,假期分享一些原理設計文章給你們</p> <p><code>原創不易,歡迎轉發,一塊兒學習(凌晨寫的,不容易哈,收藏或者點個贊吧)</code></p> <hr> <p>在常見的單頁應用中,咱們都會有一個根 App.vue 文件,裏面放置一個 router-view 而後配置路由來切換.</p> <p>不少人在子父組件嵌套關係下的生命週期鉤子函數如何應用,誰先誰後(好比哪一個用來發送請求,數據傳遞)等有所疑問。</p> <p>本文聚焦 <code>mounted</code> 事件(須要 <code>created</code> 的能夠留言哈),先拋結論:</p> <blockquote>子組件一層一層往外觸發,最終觸發根 App.vue 的 mounted</blockquote> <p>驗證的作法很簡單:</p> <blockquote>你只須要在每個組件裏面的 mounted 增長打印日誌就能夠看到了,咱們具體來看看設計原理</blockquote> <p>如今假設咱們配置了路由:</p> <p>一級是 /user/:id 二級是 profile</p>vue

const router = new VueRouter({
  routes: [
    { 
      path: '/user/:id', 
      component: User,
      children: [
        {
          // 當 /user/:id/profile 匹配成功,
          // UserProfile 會被渲染在 User 的 &lt;router-view&gt; 中
          path: 'profile',
          component: UserProfile
        }
      ]
    }
  ]
})

<p>先看一下全部的 <code>mounted</code> 最終在各自組件裏面是如何<code>被調用</code>的:</p> <p>經過 <code>vm.$options</code> 獲取組件內部的配置,而後經過 <code>call</code> 方法</p>node

<p>下面的咱們又會遇到 vnode(因此掌握它很重要,在前面的 <a href="https://segmentfault.com/a/1190000016323531">vue.js 源碼原創系列 ref 與 $refs 如何關聯</a> 也提到了一些 vnode,感興趣能夠看看),就是下面 componentVNodeHooks 裏面的 insert 函數的<code>參數</code></p>segmentfault

var componentVNodeHooks = {
  insert: function insert (vnode) {}
}

<p>裏面呢,第一步:從 vnode 裏面獲取 componentInstance</p>數組

var componentInstance = vnode.componentInstance;

<p>而後判斷 <code>_isMounted</code> 是否已經執行過 mounted (很經常使用的狀態二次肯定的變量,前面的 _ 通常表明內部使用)</p>app

if (!componentInstance._isMounted) {
  componentInstance._isMounted = true;
  callHook(componentInstance, 'mounted');
}

<p>上面咱們就用到了 <code>callHook</code> 函數了,傳入的第二個參數也正是本文討論的生命週期的 <code>mounted</code></p> <p>再日後有一個 invokeInsertHook 函數</p>函數

function invokeInsertHook (vnode, queue, initial) {
}

<p>注意一下源碼裏面的註釋:</p> <blockquote>delay insert hooks for component root nodes, invoke them after the element is really inserted</blockquote> <p>設置了 <code>pendingInsert</code>(後面會在 <code>initComponent</code> 中使用),代碼以下:</p>學習

if (isTrue(initial) &amp;&amp; isDef(vnode.parent)) {
  vnode.parent.data.pendingInsert = queue;
}

<p>內部設計:循環 queue 這個包含 vnode 的數組對象,如圖所示:</p>設計

<p>注意一下標註的 <code>data.hook</code>,下面的代碼片斷會使用到,也就是調用上面提到的 <code>componentVNodeHooks</code> 對象的 <code>insert</code>:</p>日誌

for (var i = 0; i &lt; queue.length; ++i) {
  queue[i].data.hook.insert(queue[i]);
}

<p>再往下,帶着疑問:</p> <blockquote>這個 queue 是如何生成 vnode 數組的呢?</blockquote> <p>最開始定義一個空數組:</p>code

var insertedVnodeQueue = [];

<p>在剛纔的 對象中還有 <code>init</code></p>

var componentVNodeHooks = {
  init: function init () {
    // ...
  }
}

<p><code>init</code> 函數內部定義一個 <code>child</code></p>

var child = vnode.componentInstance = createComponentInstanceForVnode(...)

<p>而後會調用一個 <code>$mount</code></p>

child.$mount(hydrating ? vnode.elm : undefined, hydrating);

<p>在函數 <code>initComponent</code> 中會用到以前的 <code>pendingInsert</code>,並且 <code>insertedVnodeQueue 這個數組</code>對象會調用 <code>push</code> 插入元素</p>

function initComponent (vnode, insertedVnodeQueue) {
  if (isDef(vnode.data.pendingInsert)) {
    insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert);
    vnode.data.pendingInsert = null;
  }
}

<p>在函數 <code>invokeCreateHooks</code> 內部<code>insertedVnodeQueue 這個數組</code>對象會調用 <code>push</code> 插入元素</p>

function invokeCreateHooks (vnode, insertedVnodeQueue) {
  i = vnode.data.hook; // Reuse variable
  if (isDef(i)) {
    if (isDef(i.insert)) { 
      insertedVnodeQueue.push(vnode); 
    }
  }
}

<p>在函數 <code>mountComponent</code> 內部當 vm.$vnode 爲 null 也會調用 <code>callHook</code>,第二個參數傳入 <code>mounted</code></p>

function mountComponent () {
  if (vm.$vnode == null) {
    vm._isMounted = true;
    callHook(vm, 'mounted');
  }
}

來源:https://segmentfault.com/a/1190000016499274

相關文章
相關標籤/搜索