当前位置: 首页 > news >正文

vue3 源码解析(6)— lifecycle 生命周期的实现

前言

对于 vue3 的生命周期,我们经常性会去疑问,生命周期有哪些呢,它是怎么去实现的, 又是什么时候调用的。

vue3 生命周期有哪些

下面这个表格列出了所有选项式api生命周期钩子和组合式api生命周期钩子,以及他们的对应关系和执行的时机。

composition apioptions api执行时机
beforeCreate初始化组件内的属性(如:data,props,watch,computed等)之前
created初始化组件内的属性(如:data,props,watch,computed等)之后
onBeforeMountbeforeMount组件开始挂载之前
onMountedmounted组件挂载之后
onBeforeUpdatebeforeUpdate组件数据更新之后,页面更新之前
onUpdatedupdated组件数据更新之后,页面更新之后
onBeforeUnmountbeforeUnmount组件即将卸载,但还未卸载
onUnmountedunmounted组件卸载之后
onErrorCapturederrorCaptured捕获了后代组件传递的错误时
onRenderTrackedrenderTracked响应式依赖被组件的渲染作用追踪后,仅开发模式下使用
onRenderTriggeredrenderTriggered响应式依赖被组件触发了重新渲染之后,仅开发模式下使用
onActivatedactivated组件被keep-alive包裹,页面从不活动状态变为活动状态执时
onDeactivateddeactivated组件被keep-alive包裹,页面从活动状态变为不活动状态执时
onServerPrefetchserverPrefetch组件实例在服务器上被渲染之前,为异步函数,仅ssr模式使用

生命周期是如何挂载到实例化的

首先,我们可以根据 onBeforeMount、onMounted 的源码定义,可以看出定义钩子函数时是直接调用 injectHook 挂载在实例化上。

import { currentInstance, setCurrentInstance } from './component'const enum LifecycleHooks {BEFORE_MOUNT = 'bm',MOUNTED = 'm',BEFORE_UPDATE = 'bu',UPDATED = 'u',BEFORE_UNMOUNT = 'bum',UNMOUNTED = 'um',
}export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
export const onMounted = createHook(LifecycleHooks.MOUNTED)
export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
export const onUpdated = createHook(LifecycleHooks.UPDATED)
export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)// 核心就是这个生命周期函数要和当前组件产生关联
function createHook (lifecycle) {return function (hook, target = currentInstance) { // hook 自定义方法// 获取到当前组件的实例和生命周期产生关联injectHook(lifecycle, hook, target)}
}/*** * @param type bm、m、bu、u等代表生命周期的简写变量* @param hook 自定义的方法* @param target 当前 vue 实例*/
function injectHook (type, hook, target) {if (target) {const hooks = target[type] || (target[type] = [])const wrappedHook = () => {setCurrentInstance(target)hook()setCurrentInstance(null)}hooks.push(wrappedHook) // 挂载的时候,直接放到对应的数组里面}
}

这段代码的作用是定义了一系列组件生命周期钩子函数,并将传入的自定义钩子函数与当前组件实例关联起来。

生命周期如何调用、触发

首先,通过 injectHook 函数我们知道,每个生命周期钩子函数都是存放到实例化的变量里面,所以,要先从实例化里面取出。例如:

const { bm, m } = instance // 获取
if (bm) {invokeArrayFns(bm)
}

其次,用户自定义的 hook 函数都是存放到对应的生命周期函数的数组中,所以调用的时候直接遍历数组执行即可。

const invokeArrayFns = (fns) => {for (let i = 0; i < fns.length; i++) {fns[i]()}
}

onBeforeMount、onMounted、onBeforeUpdate、onUpdated

从之前的篇文章中我们知道 createApp 函数最为重要的一个函数是 mount,在 mount 函数中会调用mountComponent 经行组件的挂载。我们可以从 mountComponent 函数为口入逐步分析生命周期执行的过程。

mountComponent

mountComponent 函数用于挂载组件类型的虚拟 DOM 节点。

函数的作用是创建组件实例、解析组件实例对象并设置渲染效果。

const mountComponent = (initialVNode, container) => {const instance = initialVNode.component = createComponentInstance(initialVNode)setupComponent(instance) // 解析组件的实例对象setupRenderEffect(instance, container)
}

createComponentInstance

初始化一个组件的实例对象,添加相关属性(vnode、type、props、attrs、ctx、proxy)。

const createComponentInstance = (vnode) => {const instance = {vnode,type: vnode.type, // 组件的类型props: {}, // 组件的属性attrs: {},setupState: {}, // setup返回值ctx: {}, // proxy.props.name -> proxy.nameproxy: {},render: false,isMounted: false, // 是否挂载children: []}instance.ctx = { _: instance } // 实例上下文return instance
}

setupRenderEffect

setupRenderEffect 创建一个 effect,用于在组件状态变化时触发重新渲染。

该 effect 依赖于组件实例的响应式数据,当响应式数据发生变化时,会触发该 effect,进而调用组件实例的 render 方法重新渲染组件,并将渲染结果插入到 container 容器中。

import { effect } from '@vue/reactivity'const setupRenderEffect = (instance, container) => {// 创建响应式的副作用渲染函数effect(function componentsEffect () {// 判断第一次加载// 执行render,获取方法中的依赖收集effect,属性改变再次执行if (!instance.isMounted) {// beforeMount hookconst { bm, m } = instanceif (bm) {invokeArrayFns(bm)}const proxyToUse = instance.proxy // 组件的实例const subTree = instance.subTree = instance.render.call(proxyToUse,proxyToUse) // 渲染组件生成子树vnode// 将子树vnode挂载到container上patch(null, subTree, container)instance.isMounted = true// 渲染完成 mounted hookif (m) {invokeArrayFns(m)}} else {const { u, bu } = instance// beforeUpdate hookif (bu) {invokeArrayFns(bu)}const proxyToUse = instance.proxy // 组件的实例const prevTree = instance.subTree // 保留渲染生成的子树根DOM节点const nextTree = instance.render.call(proxyToUse, proxyToUse) // 获取子组件树instance.subTree = nextTree // 更新当前的子组件树patch(prevTree, nextTree, container) // 渲染新的子组件树到容器中// update hookif (u) {invokeArrayFns(u)}}})}

在执行组件挂载之前,会检测组件实例上是否存在注册的 beforeMount 钩子函数(bm)。
如果存在,通过遍历 instance.bm 数组并使用 invokeArrayFns 方法依次执行这些钩子函数。
这样设计的原因是用户可以通过多次调用 onBeforeMount 函数来注册多个 beforeMount 钩子函数,保证它们按注册顺序依次执行。

onBeforeUnmount、onUnmounted

在之前分析 patch 函数的执行流程中我们知道,当存在旧节点并且新旧节点的类型不同时,会先卸载旧节点,然后进行新节点的渲染和挂载。用户也可以手动调用 unmount 函数经行卸载。

unmount

unmount 函数的作用是执行组件的卸载操作。

// patching & not same type, unmount old tree
if (n1 && !isSameVNodeType(n1, n2)) {anchor = getNextHostNode(n1)unmount(n1, parentComponent, parentSuspense, true)n1 = null
}

unmount 函数的作用包括以下几个方面:

  1. 从父组件中移除:unmount 函数会将组件从其父组件中移除。这意味着组件将不再在父组件的模板中渲染,并且在父组件的组件实例中,该组件的相关引用将被清除。

  2. 卸载组件实例:unmount 函数会执行组件实例的卸载过程。在卸载过程中,vue 3 会依次触发生命周期钩子函数,包括 onBeforeUnmount 和 unmounted。你可以在这些钩子函数中执行一些清理操作或释放资源的操作。

  3. 移除 DOM 元素:unmount 函数会将组件的根 DOM 元素从 DOM 树中移除,以及解绑组件与其根 DOM 元素之间的关联。这样,组件的模板内容将不再显示在页面上,并且与 DOM 相关的事件监听器和其他绑定也将被清除。

在 unmount 函数中如果 ShapeFlags 类型是 COMPONENT 的话会执行 unmountComponent 函数经行组件的卸载。

unmountComponent

unmountComponent 函数的作用是卸载组件实例。为了便于理解,以下代码为简化之后的代码:

const unmountComponent = (instance,parentSuspense,doRemove,
) => {const { bum, um } = instance// beforeUnmount hookif (bum) {invokeArrayFns(bum)}// stop effects in component scopescope.stop()// unmounted hookif (um) {queuePostRenderEffect(um, parentSuspense) // 这里会循环 um 里面的函数}
}

总结

希望通过本篇文章可以帮助读者更好的了解生命周期的执行过程。

相关文章:

vue3 源码解析(6)— lifecycle 生命周期的实现

前言 对于 vue3 的生命周期&#xff0c;我们经常性会去疑问&#xff0c;生命周期有哪些呢&#xff0c;它是怎么去实现的&#xff0c; 又是什么时候调用的。 vue3 生命周期有哪些 下面这个表格列出了所有选项式api生命周期钩子和组合式api生命周期钩子&#xff0c;以及他们的…...

three.js CSS2DRenderer、CSS2DObject渲染HTML标签

有空的老铁关注一下我的抖音&#xff1a; 效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red;position: relative;"><…...

SECS/GEM300和半导体e84控制器

SECS&#xff08;SEMI EQUIPMENT COMMUNICATIONS STANDARD 2&#xff09;半导体设备通讯标准 GEM&#xff08;Generic Equipment Model&#xff09;定义了Fab中各个场景下设备行为及其所使用SECS消息。 GEM300也称为300mm标准&#xff0c;FAB是12寸设备的处理作业规范。主要包…...

k8s二进制及负载均衡集群部署详解

目录 常见部署方式 二进制部署流程 环境准备 操作系统初始化配置 关闭防火墙 配置SELinux 关闭SWAP 根据规划设置主机名 在master添加hosts&#xff0c;便于主机名解析 调整内核参数 配置时间同步 部署docker引擎 在所有node节点部署docker引擎 部署etcd集群 签发…...

【Django开发】0到1开发美多商城项目第3篇:用户注册业务实现(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目相关知识。项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现前后端…...

免费的ppt网站分享

前言 相信大学生们深有体会&#xff0c;对于学校而言&#xff0c;好像是任何活动都需要我们做ppt&#xff0c;当你拿着自己辛苦做的ppt去展示现场的时候&#xff0c;你看到别人的ppt比你的还好&#xff0c;此时心情就是毙&#xff0c;当你知道人家不过是仅仅的1个小时不到就完成…...

基于Transformer结构的扩散模型综述

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…...

POI操作word表格,添加单元格,单元格对齐方法(不必合并单元格)

添加单元格&#xff0c;直接对row进行create新的cell&#xff0c;则会导致新创建的单元格与前面的单元格不对齐的现象。 //表格信息XWPFTable table doc.createTable();table.setWidth("100%");//第一行XWPFTableRow row0table.getRow(0);XWPFTableCell cell00row0.…...

maven代码规范检查(checkstyle、findbugs)

maven代码规范检查 前言一、使用checkstyle插件1. maven-checkstyle-plugin 介绍2. 接入方式3. 如何排除某个类、包下面的文件不进行检查使用suppressionsLocation 4. 如何关闭 二、使用findbugs插件1.findbugs-maven-plugin介绍2. 接入方式3. 如何排除某个类、包下面的文件不进…...

妙用Java反射,让代码更加优雅

最近在改公司项目bug&#xff0c;需要修改别人的代码。在读别人的源码时感觉到反射真的是能够极大的提高代码的优雅性&#xff0c;在某些特定场景能极大的简化代码的编写。因此写了这篇文章用以记录分享。 我们先还原一下场景&#xff0c;在做数据展示的时候&#xff0c;需要处…...

实习日志10

1.用户信息 1.1.在用户管理中编辑用户信息 1.2.绑定公司id 1.3.显示在页面 2.修改识别逻辑 2.1.分析 先识别&#xff0c;再判断&#xff0c;清空键把识别结果清空 2.2.写码 修改了发票识别逻辑&#xff0c;略... 3.接高拍仪 3.1.js引入报错 分析&#xff1a; 遇到的错误…...

配置alias(设置别名@)

Vite配置alias需要两步进行&#xff08;TS项目&#xff09; 1、修改vite.config.ts&#xff08;让程序支持&#xff09;2、修改tsconfig.json&#xff08;让编辑器支持&#xff09;修改vite.config.ts import { defineConfig } from vite import path from path ​ function…...

【动态规划】【数学】1388. 3n 块披萨

作者推荐 【动态规划】【字符串】【表达式】2019. 解出数学表达式的学生分数 本文涉及知识点 动态规划汇总 LeetCode1388 3n 块披萨 给你一个披萨&#xff0c;它由 3n 块不同大小的部分组成&#xff0c;现在你和你的朋友们需要按照如下规则来分披萨&#xff1a; 你挑选 任…...

CS144--Chapter0--wsl2+docker环境搭建

我的笔记本配置 荣耀magicbook16&#xff0c;容量是500G&#xff0c;芯片是R7-5800 由于笔记本容量较小&#xff0c;因此考虑这个方案&#xff0c;对于台式机用户&#xff0c;建议可以直接用虚拟机或者双系统。 前言 斯坦福官网给出的方法是用他们的镜像&#xff08;基于Ubu…...

MGRE实验报告二

实验要求&#xff1a; 实验预览图&#xff1a; 实验分析&#xff1a; 1、对R1-R5配置IP地址&#xff0c;同时R1-R5每个路由器各有一个环回 2.1、对R1、R3、R4路由器开启虚拟接口1&#xff0c;分别配置隧道IP、接口封装协议&#xff0c;接口类型、定义封装源、开启伪广播功能&…...

算法设计与分析实验:最短路径算法

一、网络延迟时间 力扣第743题 本题采用最短路径的思想进行求解 1.1 具体思路 &#xff08;1&#xff09;使用邻接表表示有向图&#xff1a;首先&#xff0c;我们可以使用邻接表来表示有向图。邻接表是一种数据结构&#xff0c;用于表示图中顶点的相邻关系。在这个问题中&am…...

共用体与枚举法,链表的学习

结构体注意事项&#xff1a; 1.结构体类型可以定义在main函数里面&#xff0c;但是此时的作用域就被限定在该函数中 2.结构体的的的定义的形式&#xff1a;a.先定义类型&#xff0c;后定义变量-----struct stu s b.定义类型的同时&#xff0c;定义了变量&#xff1a;struct…...

SG2520CAA汽车用晶体振荡器

爱普生SG2520CAA是简单的封装晶体振荡器&#xff08;SPXO&#xff09;&#xff0c;具有CMOS输出&#xff0c;这款SPXO是汽车和高可靠性应用的理想选择&#xff0c;符合AEC-Q200标准&#xff0c;功耗低&#xff0c;工作电压范围为1.8 V ~ 3.3 V类型&#xff0c;宽工作温度-40℃~…...

使用pip将第三方依赖包下载到本地指定位置

pip download -d save_path packages -d:后面接下载包路径(save_path) packages:安装包名称...

C语言探索:水仙花数的奥秘与计算

摘要&#xff1a; 水仙花数&#xff0c;一种特殊的三位数&#xff0c;其各位数字的立方和等于该数本身。本文将详细介绍水仙花数的定义、性质&#xff0c;以及如何使用C语言来寻找100至999范围内的水仙花数。 目录 一、水仙花数的定义与性质 二、用C语言寻找100至999范围内的…...

WarcraftHelper终极指南:5大核心功能让魔兽争霸3在现代系统完美运行

WarcraftHelper终极指南&#xff1a;5大核心功能让魔兽争霸3在现代系统完美运行 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper WarcraftHelper是一款…...

临近起飞,在哪个平台更容易捡漏特价机票?2026年实测指南

“机票越临近起飞越便宜”——这个说法你一定听过。每逢假期临近&#xff0c;总有人在社交媒体上分享自己“起飞前两小时抢到白菜价机票”的神奇经历。但当你真的想在清明、五一出行前“赌一把”时&#xff0c;往往发现价格不仅没降&#xff0c;反而翻倍了。那么问题来了&#…...

ABC系统实战指南:革新数字电路设计的逻辑综合与形式验证技术突破

ABC系统实战指南&#xff1a;革新数字电路设计的逻辑综合与形式验证技术突破 【免费下载链接】abc ABC: System for Sequential Logic Synthesis and Formal Verification 项目地址: https://gitcode.com/gh_mirrors/ab/abc 在现代集成电路设计流程中&#xff0c;工程师…...

交易数据一致性保障:大数据环境下的挑战

交易数据一致性保障:大数据环境下的挑战 1. 引入与连接:数字世界的"货币守卫" 想象一下:当你在电商平台下单支付后,银行显示扣款成功,但商家却显示支付失败;或者在股票交易中,你看到的股价与实际成交价格存在差异。这些看似微小的数据不一致,可能导致企业声…...

OpenCore 辅助工具(OCAT):跨平台开源配置工具的零基础上手指南

OpenCore 辅助工具&#xff08;OCAT&#xff09;&#xff1a;跨平台开源配置工具的零基础上手指南 【免费下载链接】OCAuxiliaryTools Cross-platform GUI management tools for OpenCore&#xff08;OCAT&#xff09; 项目地址: https://gitcode.com/gh_mirrors/oc/OCAuxili…...

保姆级教程:CLIP-GmP-ViT-L-14图文匹配工具一键部署,小白也能玩转AI识图

保姆级教程&#xff1a;CLIP-GmP-ViT-L-14图文匹配工具一键部署&#xff0c;小白也能玩转AI识图 你是不是经常好奇&#xff0c;AI到底是怎么看懂图片的&#xff1f;给它一张照片和几个文字描述&#xff0c;它怎么知道哪个描述最贴切&#xff1f;今天&#xff0c;我就带你亲手搭…...

luci-app-unblockneteasemusic 插件完整技术指南:实现网易云音乐播放限制解除

luci-app-unblockneteasemusic 插件完整技术指南&#xff1a;实现网易云音乐播放限制解除 【免费下载链接】luci-app-unblockneteasemusic [OpenWrt] 解除网易云音乐播放限制 项目地址: https://gitcode.com/gh_mirrors/lu/luci-app-unblockneteasemusic luci-app-unblo…...

第4章 编码规范-4.3 导入规范

导入语句包括import语句和from…import语句&#xff0c;该语句需要位于编码注释和文件注释之后&#xff0c;全局变量和常量之前。建议每一条导入语句只导入一个模块。示例代码如下&#xff1a;# 资源包\Code\chapter4\4.3\0406.py# 建议每一条导入语句只导入一个模块import rei…...

Agent 语音交互如何更稳、更快?一次高并发消息链路优化实践

作者&#xff1a;雀贤、文婷、复礼、稚柳 随着大语言模型&#xff08;LLM&#xff09;、语音识别&#xff08;ASR&#xff09;、语音合成&#xff08;TTS&#xff09;等能力逐步成熟&#xff0c;AI Agent 开始从文本交互走向语音交互&#xff0c;典型场景包括 AI 教师、AI 情感…...

从ImageNet到CV落地:深度解读AlexNet的6个工程优化技巧

从AlexNet到现代CV工程&#xff1a;6个历久弥新的优化策略解析 当AlexNet在2012年ImageNet竞赛中以压倒性优势夺冠时&#xff0c;它带来的不仅是准确率的飞跃&#xff0c;更是一套影响深远的工程实践方法论。十年过去&#xff0c;尽管网络架构已迭代数十代&#xff0c;但AlexNe…...