千家信息网

vue中的mounted钩子怎么用

发表于:2025-11-13 作者:千家信息网编辑
千家信息网最后更新 2025年11月13日,这篇文章主要为大家展示了"vue中的mounted钩子怎么用",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"vue中的mounted钩子怎么用"这篇文章吧。
千家信息网最后更新 2025年11月13日vue中的mounted钩子怎么用

这篇文章主要为大家展示了"vue中的mounted钩子怎么用",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"vue中的mounted钩子怎么用"这篇文章吧。

注:阅读本文需要对vue的patch流程有较清晰的理解,如果不清楚patch流程,建议先了解清楚这个流程再阅读本文,否则可能会感觉云里雾里。

聊之前我们先看一个场景

如上所示,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组件先创建,再挂载,然后到b组件重复以上流程。因为从patch的方法里面可以知道,组件created后,再走到insert进父容器的过程,是一个同步的流程,只有这个流程走完后,才会遍历到b组件,走b组件的渲染流程。

实际上浏览器打印出来的顺序是aC→bC→aM→bM,也就是两个created先执行,才到mounted执行,和上面的分析相悖。这里先说原因,子组件从created到insert进父容器的过程还是同步的,但是insert进父容器后,也可以理解为子组件mounted,并没有马上调用mounted生命周期钩子。下面从源码角度分析一下:

先大概回顾一下子组件渲染流程,patch函数调用createElm创建真实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);                                // 最终组件创建完后会走到这里 把组件对应的el插入到父节点        insert(parentElm, vnode.elm, refElm);        if (isTrue(isReactivated)) {          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);        }        return true      }    }  }

createComponent里面就把组件对应的el插入到父节点,最后会返回到patch调用栈,调用

invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);

因为子组件有vnode.parent所以会走一个分支,但是我们也看看第二个分支调用的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上,在组件创建过程中,createComponent方法里面有一个调用

installComponentHooks,在这里把insert钩子注入了。这个方法实际定义在componentVNodeHooks对象里面,可以看到这个insert里面调用了callHook(componentInstance, 'mounted'),这里实际上就是调用子组件的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 */);      }    }  },

再来看看这个方法,子组件走第一个分支,仅仅执行了一行代码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为一个空数组var insertedVnodeQueue = [];

源码里面再搜索insertedVnodeQueue ,可以看到有这样一段逻辑,initComponent还是在createComponent里面调用的

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这个数组每一项push到当前vnode的insertedVnodeQueue中,注意这里是通过apply的方式,所以是把 vnode.data.pendingInsert这个数组每一项都push,而不是push pendingInsert这个列表进去。也就是说在这里,组件把他的子组件的insertedVnodeQueue里面的item收集了,因为渲染是一个深度递归的过程,所有最后根组件的insertedVnodeQueue能拿到所有子组件的insertedVnodeQueue里面的每一项。

从invokeInsertHook的queue[i].data.hook.insert(queue[i]) 这一行可以看出,insertedVnodeQueue里面的item应该是vnode。源码中搜索insertedVnodeQueue.push ,可以发现是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中调用的。

到这里就很清晰,子组件insert进父节点后,并不会马上调用mounted钩子,而是把组件对应到vnode插入到父vnode的insertedVnodeQueue中,层层递归,最终根组件拿到所有子组件的vnode,再依次循环遍历,调用vnode的insert钩子,从而调用了mounted钩子。这里是先进先出的,第一个被push进去的第一个被拿出来调用,所以最深的那个子组件的mounted先执行。最后附上一张源码调试的图,可以清晰的看到根组件的insertedVnodeQueue是什么内容。

至于为什么vue要这样设计,是因为挂载是先子后父的,子组件插入到了父节点,但是父节点还没有真正插入到页面中,如果这时候立马调用子组件的mounted,对框架使用者来说可能会造成困惑,因为子组件调用mounted的时候并没有真正渲染到页面中,而且此时也肯定也无法通过document.querySelector的方式操作dom。

以上是"vue中的mounted钩子怎么用"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

组件 流程 钩子 方法 节点 内容 实际 过程 一行 两个 也就是 分支 周期 容器 数组 时候 源码 生命 篇文章 还是 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 方舟官方服务器怎么有等级限制 天津市河东区网络安全工作会议 计算机网络技术可以去哪里实习 网络安全教育宣传单家长留言 网络安全教育考试a卷 新增考研专业网络安全 军事网络安全与战略教育 一般软件开发价格评估 数据库概念模型设计方法 uu加速器香港服务器炸了 怎么关闭服务器安全配置 阿里云开服务器 山东大学数据库实验七答案 软件开发量化评估 管理会计分析软件开发商 软件开发合伙人好找工作吗 工会网络安全宣传周活动简报 网络安全问题的研究课题研究 笔记本电脑服务器停止响应 计算机网络技术专科女生 网络安全法有什么意义 db2数据库修改表空间路径 feature 软件开发 数据库全码的例子 数据库表按时间保存表 深圳市安居兔网络技术 肇庆微信软件开发订制 中国网络安全专家评论 vs2017数据库怎么用 百度文库软件开发流程图
0