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


    大家中秋假期快乐,假期分享一些原理设计文章给大家

    原创不易,欢迎转发,一起学习(凌晨写的,不容易哈,收藏或者点个赞吧)


    在常见的单页应用中,我们都会有一个根 App.vue 文件,里面放置一个 router-view 然后配置路由来切换.

    很多人在子父组件嵌套关系下的生命周期钩子函数如何应用,谁先谁后(比如哪个用来发送请求,数据传递)等有所疑问。

    本文聚焦 mounted 事件(需要 created 的可以留言哈),先抛结论:

    子组件一层一层往外触发,最终触发根 App.vue 的 mounted

    验证的做法很简单:

    你只需要在每一个组件里面的 mounted 增加打印日志就可以看到了,我们具体来看看设计原理

    现在假设我们配置了路由:

    一级是 /user/:id 二级是 profile

    
    const router = new VueRouter({
      routes: [
        { 
          path: '/user/:id', 
          component: User,
          children: [
            {
              // 当 /user/:id/profile 匹配成功,
              // UserProfile 会被渲染在 User 的 <router-view> 中
              path: 'profile',
              component: UserProfile
            }
          ]
        }
      ]
    })
    

    先看一下所有的 mounted 最终在各自组件里面是如何被调用的:

    通过 vm.$options 获取组件内部的配置,然后通过 call 方法

    下面的我们又会遇到 vnode(所以掌握它很重要,在前面的 vue.js 源码原创系列 ref 与 $refs 如何关联 也提到了一些 vnode,感兴趣可以看看),就是下面 componentVNodeHooks 里面的 insert 函数的参数

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

    里面呢,第一步:从 vnode 里面获取 componentInstance

    
    var componentInstance = vnode.componentInstance;
    

    然后判断 _isMounted 是否已经执行过 mounted (很常用的状态二次确定的变量,前面的 _ 一般代表内部使用)

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

    上面我们就用到了 callHook 函数了,传入的第二个参数也正是本文讨论的生命周期的 mounted

    再往后有一个 invokeInsertHook 函数

    
    function invokeInsertHook (vnode, queue, initial) {
    }
    

    注意一下源码里面的注释:

    delay insert hooks for component root nodes, invoke them after the element is really inserted

    设置了 pendingInsert(后面会在 initComponent 中使用),代码如下:

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

    内部设计:循环 queue 这个包含 vnode 的数组对象,如图所示:

    注意一下标注的 data.hook,下面的代码片段会使用到,也就是调用上面提到的 componentVNodeHooks 对象的 insert

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

    再往下,带着疑问:

    这个 queue 是如何生成 vnode 数组的呢?

    最开始定义一个空数组:

    
    var insertedVnodeQueue = [];
    

    在刚才的 对象中还有 init

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

    init 函数内部定义一个 child

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

    然后会调用一个 $mount

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

    在函数 initComponent 中会用到之前的 pendingInsert,而且 insertedVnodeQueue 这个数组对象会调用 push 插入元素

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

    在函数 invokeCreateHooks 内部insertedVnodeQueue 这个数组对象会调用 push 插入元素

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

    在函数 mountComponent 内部当 vm.$vnode 为 null 也会调用 callHook,第二个参数传入 mounted

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

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

  • 相关阅读:
    C语言指针入门
    c的动态内存管理
    汇编入门基础与helloworld
    汇编1
    汇编2——完整的例子集合
    算法与数据结构——选择,插入,希尔排序
    MySQL
    MySQL 笔记
    CSS样式优先级
    GIT使用笔记
  • 原文地址:https://www.cnblogs.com/datiangou/p/10136685.html
Copyright © 2020-2023  润新知