路由场景下父子组件的生命周期顺序来个刨根问底

<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

相关文章
相关标签/搜索