Vue3 源码解读系列(八)——生命周期

生命周期

在这里插入图片描述

正常的生命周期

// 注册钩子函数
const onBeforeMount = createHook('bm'/* BEFORE_MOUNT */)
const onMounted = createHook('m'/* MOUNTED */)
const onBeforeUpdate = createHook('bu'/* BEFORE_UPDATE */)
const onUpdated = createHook('u'/* UPDATED */)
const onBeforeUnmount = createHook('bum'/* BEFORE_UNMOUNT */)
const onUnmounted = createHook('um'/* UNMOUNTED */)
const onRenderTriggered = createHook('rtg'/* RENDER_TRIGGERED */)
const onRenderTracked = createHook('rtc'/* RENDER_TRACKED */)
const onErrorCaptured = (hook, target = currentInstance) => {injectHook('ec'/* ERROR_CAPTURED */, hook, target)
}/*** 创建钩子函数* createHook 只是对 injectHook 的封装,区别只有第一个参数不同*/
function createHook(lifecycle) {return function (hook, target = currentInstance) {// 通过 injectHook 注册钩子函数injectHook(lifecycle, hook, target)}
}/*** 注册钩子函数* injectHook 主要是对用户注册的 hook 进行封装,然后添加到一个数组中,把数组保存到当前组件实例 target 上* 钩子函数必须要保存在当前的组件实例上,通过不同的字符串 key 找到对应的钩子函数数组并执行*/
function injectHook(type, hook, target = currentInstance, prepend = false) {const hooks = target[type] || (target[type] = [])// 封装 hook 钩子函数并缓存const wrappedHook = hook.__weh || (hook.__weh = (...args) => {if (target.isUnmounted) return// 停止依赖收集(因为执行生命周期时往往已经执行过依赖收集了,所以不需要在执行生命周期时继续依赖收集,这会损耗性能)pauseTracking()// 在钩子函数执行的时候,为了确保此时的 currentInstance 和注册钩子函数是一致的,会通过 setCurrentInstance 设置 target 为当前的组件实例setCurrentInstance(target)// 执行钩子函数const res = callWithAsyncErrorHandling(hook, target, type, args)setCurrnetInstance(null)// 恢复依赖收集resetTracking()return res})if (prepend) {hooks.unshift(wrappedHook)} else {hook.push(wrappedHook)}
}/*** 组件副作用渲染函数*/
const setupRenderEffect = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) => {// 创建响应式的副作用渲染函数instance.update = effect(function componentEffect() {// 组件挂载部分// 父 beforeMount -> 子 beforeMount -> 子 mounted -> 父 mountedif (!instance.isMounted) {// 获取组件实例上通过 onBeforeMount 钩子函数和 onMounted 注册的钩子函数const { bm, m } = instance// 渲染组件生成子树 vnodeconst subTree = (instance.subTree = renderComponentRoot(instance))// 执行 beforeMount 钩子函数if (bm) {// bm 是一个数组,因为用户可以通过指定多个 onBeforeMount 函数注册多个钩子函数,因此这里是遍历数组依次执行invokeArrayFus(bm)}// 把子树 vnode 挂载到 container 中patch(null, subTree, container, anchor, instance, parentSuspense, isSVG)// 保留渲染生成的子树根 DOM 节点initialVNode.el = subTree.el// 执行 mounted 钩子函数if (m) {queuePostRenderEffect(m, parentSuspense)}instance.isMounted = true}// 组件更新部分// 组件的更新只涉及当前组件else {// 获取组件实例上通过 onBeforeUpdate 钩子函数和 onUpdated 注册的钩子函数let { next, vnode, bu, u } = instance// next 表示新的组件 vnodeif (next) {// 更新组件 vnode 节点信息updateComponentPreRender(instance, next, optimized)} else {next = vnode}// 渲染新的子树 vnodeconst nextTree = renderComponentRoot(instance)// 缓存旧的子树 vnodeconst prevTree = instance.subTree// 更新子树 vnodeinstance.subTree = nextTREE// 执行 beforeUpdate 钩子函数if (bu) {invokeArrayFns(bu)}// 组件更新核心逻辑,根据新旧子树 vnode 做 patchpatch(prevTree, nextTree,// 如果在 teleport 组件中父节点可能已经改变,所以容器直接找旧树 DOM 元素的父节点hostParentNode(prevTree.el),// 缓存更新后的 DOM 节点getNextHostNode(prevTree), instance, parentSuspense, isSVG)// 缓存更新后的 DOM 节点next.el = nextTree.el// 执行 updated 钩子函数// 注意:不要在 updated 钩子函数中更改数据,因为会再次触发组件更新!!!if (u) {queuePostRenderEffect(u, parentSuspense)}}}, prodEffectOptions)
}/*** 组件副作用渲染函数 - 组件销毁部分* 父 beforeUnmount -> 子 beforeUnmount -> 子 unmounted -> 父 unmounted*/
const unmountComponent = (instance, parentSuspense, doRemove) => {const { bum, effects, update, subTree, um } = instance// 执行 beforeUnmount 钩子函数if (bum) {invokeArrayFns(bum)}// 清理组件引用的 effects 副作用函数if (effects) {for (let i = 0; i < effects.length; i++) {stop(effects[i])}}// 如果一个异步组件在加载前就销毁了,则不会注册副作用渲染函数if (update) {stop(update)// 调用 unmount 销毁子树unmount(subTree, instance, parentSuspense, doRemove)}// 执行 unmounted 钩子函数,通过递归的方式遍历子树销毁子节点if (um) {queuePostRenderEffect(um, parentSuspense)}
}

捕获后代组件错误的生命周期

/*** 捕获后代组件的错误 - onErrorCaptured*/
function handleError(err, instance, type) {const contextVNode = instance ? instance.vnode : nullif (instance) {let cur = instance.parentconst exposedInstance = instance.proxy // 为了兼容 2.x 版本,暴露组件实例给钩子函数// 获取错误信息const errorInfo = (process.env.NODE_ENV !== 'production') ? ErrorTypeStrings[type] : type// 尝试向上查找所有父组件,执行 errorCaptured 钩子函数while (cur) {const errorCapturedHooks = cur.ec// 如果存在则遍历执行if (errorCapturedHooks) {for (let i = 0; i < errorCapturedHooks.length; i++) {// 判断 errorCaptured 钩子函数返回 true,则停止向上查找if (errorCapturedHooks[i](err, exposedInstance, errorInfo)) return}}cur = cur.parent}}// 如果整个链路上都没有能够正确处理错误的 errCaptured 钩子函数,则控制台输出未处理的错误logError(err, type, contextVNode)
}

开发环境下调试的生命周期

/*** 创建开发环境的副作用函数的配置* 实际上是在 track、trigger 阶段添加副作用函数并执行*/
function createDevEffectOptions(instance) {return {scheduler: queueJob,onTrack: instance.rtc ? e => invokeArrayFns(instance.rtc, e) : void 0,onTrigger: instance.rtg ? e => invokeArrayFns(instance.rtg, e) : void 0}
}/*** track 的实现*/
function track(target, type, key) {// 执行一些依赖收集的操作// ...// 执行完依赖收集后执行 onTrack 函数,遍历执行 renderTracked 钩子函数if (!dep.has(activeEffect)) {dep.add(activeEffect)activeEffect.deps.push(dep)// 在非生产环境下检测当前 activeEffect 的配置有没有定义 onTrack 函数,如果有,则执行该方法if ((process.env.NODE_ENV !== 'production') && activeEffect.options.onTrack) {// 执行 onTrack 函数activeEffect.options.onTrack({effect: activeEffect,target,type,key})}}
}/*** trigger 的实现*/
function trigger(target, type, key, newValue) {// 添加要运行的 effects 集合,然后遍历执行// ...// 在非生产环境下检测待执行的 effect 配置是否定义 onTrigger 函数,如果有,则执行该方法const run = (effect) => {if ((process.env.NODE_ENV !== 'production') && effect.options.onTrigger) {// 执行 onTriggereffect.options.onTrigger({effect,target,key,type,newValue,oldValue,oldTarget})}if (effect.options.scheduler) {effect.options.scheduler(effect)} else {effect()}}// 遍历执行 effectseffects.forEach(run)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/150663.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

商城免费搭建之java商城 java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

java桌面程序

目标之一是把打印导出的功能最终用java实现一套&#xff0c;首先选定javafx&#xff0c;因为idea默认创建工程就带的javafx&#xff0c;没找到swing。 创建工程&#xff0c;这里要选1.8&#xff0c;高版本jdk默认不带fx 实现主界面的代码 package sample;import javafx.app…

spring boot集成quartz

目录 1.定时任务实现 2.quartz说明 3.存储方式 4.示例 5.定时任务的重新定制&#xff0c;恢复&#xff0c;暂停及删除 1.定时任务实现 定时任务的实现方式有很多&#xff0c;如下&#xff1a; 1.启动类中添加EnableScheduling&#xff0c;开启定时任务功能&#xff0c;然…

将word中的表格无变形的弄进excel中

在上篇文章中记录了将excel表拷贝到word中来&#xff1a; 记录将excel表无变形的弄进word里面来-CSDN博客 本篇记录&#xff1a;将word中的表格无变形的弄进excel中。 1.按F12&#xff0c;“另存为...”&#xff0c;保存类型&#xff1a;“单个文件页面”&#xff0c;保存。…

numpy报错:AttributeError: module ‘numpy‘ has no attribute ‘float‘

报错&#xff1a;AttributeError: module numpy has no attribute float numpy官网&#xff1a;NumPy 报错原因&#xff1a;从numpy1.24起删除了numpy.bool、numpy.int、numpy.float、numpy.complex、numpy.object、numpy.str、numpy.long、numpy.unicode类型的支持。 解决办法…

如果K8s出现问题,你可以从这9个方面排查

K8s问题的排查 1. POD启动异常、部分节点无法启动pod 2. 审视集群状态 3. 追踪事件日志 4. 聚焦Pod状态 5. 检查网络连通性 6. 审视存储配置 7. 研究容器日志 8. K8S集群网络通信 9. 问题&#xff1a;Service 是否通过 DNS 工作&#xff1f; 10. 总结 1. POD启动异常、…

JS中字符串与ASCII码相互转换,前端如何解决秘钥非明文存储

最近遇到一个问题&#xff0c;就是项目组要求秘钥等信息不能明文写在文件里&#xff0c;但是前端有时候是无法避免要写的&#xff0c;像有些加密方法需要和后端匹配的秘钥&#xff0c;后面组里的同事提出&#xff0c;可以将字符串转换成ASCII码再加上其他的调整&#xff0c;比如…

Java-方法的重写

【1】重写&#xff1a; 发生在子类和父类中&#xff0c;当子类对父类提供的方法不满意的时候&#xff0c;要对父类的方法进行重写。 【2】重写有严格的格式要求&#xff1a; 子类的方法名字和父类必须一致&#xff0c;参数列表&#xff08;个数&#xff0c;类型&#xff0c…

Django 入门学习总结4

视图是Django应用程序在Python语言中提供特定的方法并对应于有特定的模板的网页。网页的页面通过视图的方式进行跳转。 在投票系统中&#xff0c;有四个视图&#xff1a; 首页视图&#xff0c;显示最新的问题列表。细节视图&#xff0c;显示问题文本&#xff0c;通过表单可以…

阿里国际站(直通车)

1.国际站流量 2.直通车即P4P&#xff08;pay for performance点击付费&#xff09; 2.1直通的含义&#xff1a;按点击付费&#xff0c;通过自助设置多维度展示产品信息&#xff0c;获得大量曝光吸引潜在买家。 注意&#xff1a;中国大陆和尼日利尼地区点击不扣费。 2.2扣费规…

Apache Doris (五十四): Doris Join类型 - Bucket Shuffle Join

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录

Python的机器学习库:Sklearn库

随着机器学习的发展&#xff0c;各种强大的机器学习库涌现出来&#xff0c;使开发人员能够更轻松地构建和应用机器学习模型。其中&#xff0c;Scikit-learn&#xff08;简称Sklearn&#xff09;作为Python中最受欢迎和广泛使用的机器学习库之一&#xff0c;提供了丰富的功能和算…

linux结束 java jar

要在 Linux 中终止正在运行的 Java 进程&#xff0c;首先需要找到该进程的进程 ID&#xff08;PID&#xff09;。你可以使用 ps 命令来列出正在运行的进程&#xff0c;并通过 grep 命令过滤出与 Java 相关的进程。以下是一些可能的方法&#xff1a;### 方法 1&#xff1a;使用 …

/etc/environment,/etc/profile区别

/etc/environment 和 /etc/profile 是 Linux 系统中用于定义环境变量和启动脚本的两个不同的配置文件。它们的作用和应用场景有所区别&#xff1a; /etc/environment&#xff1a; 这是系统范围内的环境配置文件。它是由 PAM (Pluggable Authentication Module) 模块读取的&…

vue+element实现多级表头加树结构

标题两种展示方式 方式一 完整代码: <template><div class"box"><el-tableref"areaPointTable":data"tableData"border:span-method"objectSpanMethod":header-cell-style"tableHeaderMerge"><el-ta…

mac中安装Homebrew

1、Homebrew是什么&#xff1f; 软件安装管理工具 2、先检查电脑中是否已经安装了Homebrew 打开终端输入&#xff1a;brew 提示命令没有找到&#xff0c;说明电脑没有安装Homebrew 如果提示上述图片说明Homebrew已经安装成功 3、安装Homebrew 进入https://brew.sh/ 复制的命…

基于多种设计模式重构代码(工厂、模板、策略)

基于多种设计模式重构代码 现状 系统目前支持三种业务流程&#xff0c;业务A&#xff0c; 业务B&#xff0c;业务C&#xff0c;每个流程有相同的业务逻辑&#xff0c;也包含很多的特性化业务。由于之前业务流程的开发是快速迭代的&#xff0c;而且迭代了很多次&#xff0c;开发…

【邻接表特点,邻接表的代码实现】

文章目录 邻接表特点邻接矩阵和邻接表表示法的关系 邻接表&#xff08;Adjacency List&#xff09; 是图的一种链式存储结构。 #include<iostream> using namespace std;#define MVNum 100 //最大顶点数 #define OtherInfo int //权值 #define VerTexType int//顶点的指针…

一道好题——分治

一道好题应该有一个简洁的题面。 有一个长度为 n&#xff0c;初始全为 0 的序列 a&#xff0c;另有一个长度为 n 的序列 b&#xff0c;你希望将 a 变成 b&#xff0c;你可以执行如下两种操作&#xff1a; 1 x&#xff1a;将 a 中所有值为 x 的数 11。 2 x&#xff1a;将 a 中下…

Android开发APP显示头部Bar

Android开发显示头部Bar 需求&#xff1a; 显示如下图&#xff1a; 显示头部Bar&#xff0c;颜色也能自定义。 解决方案 这个修改是在如下三个文件里进行修改&#xff1a; 按顺序修改&#xff1a; themes.xml(night): <resources xmlns:tools"http://schemas.andr…