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…

将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类型的支持。 解决办法…

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;提供了丰富的功能和算…

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/ 复制的命…

Android开发APP显示头部Bar

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

持续集成交付CICD:Jenkins Sharedlibrary 共享库

目录 一、理论 1.共享库 2.共享库配置 3.使用共享库 4.共享库扩展 二、实验 1.连接共享库 2.使用共享库 三、问题 1.路径报错 2.readJSON 报错 一、理论 1.共享库 &#xff08;1&#xff09;概念 1&#xff09;共享库这并不是一个全新的概念&#xff0c;其实在编…

央国企数字化转型难在哪?为什么要数字化转型?

科技在发展&#xff0c;技术在升级&#xff0c;全球信息化、数字化的步伐在加快&#xff0c;企业想要在未来的发展中抓住机会&#xff0c;更好地发展壮大&#xff0c;就需要加快企业数字化转型的速度&#xff0c;才能立足于信息化、数字化时代&#xff0c;央国企作为企业中的一…

Vue3 项目修改index.html的 title

实现思路 通过插件 vite-plugins-html 进行参数配置&#xff0c;html 中使用参数&#xff0c;实现配置安装插件 $ npm install vite-plugins-html --save-devvite.config.js 中的配置 // 可以动态处理html文件内容的 import { createHtmlPlugin } from vite-plugin-htmlexpo…

Echarts 实现两两柱图重叠(背景和实际值柱图)

Echarts实现两两重叠柱状图_echarts 重叠柱状图_Web_阿凯的博客-CSDN博客 引用启发的博客 先来效果&#xff1a; option {backgroundColor: #03213D,animation: true, // 控制动画是否开启animationDuration: 1000, // 动画的时长, 它是以毫秒为单位animationDuration: func…

KaiwuDB 监控组件及辅助 SQL 调优介绍

一、介绍 KaiwuDB 具备完善的行为数据采集功能&#xff0c;此功能要求 KaiwuDB 数据库系统 C/E/T 端不同进程的不同维度的指标采集功能十分完善&#xff1b;在不同进程完成指标采集后&#xff0c;会通过 Opentelemetry 和 Collector 将指标存入 Prometheus&#xff0c;以便查找…

KiCad源代码研究:KiCad是如何渲染和绘图的。

common.json文件中appearance.show_scrollbars common.json对应于代码的common_settings 1.EDA_DRAW_PANEL_GAL类 EDA_DRAW_PANEL_GAL类中定义了绘图的基本要素&#xff1a; /// Interface for drawing objects on a 2D-surfaceKIGFX::GAL* m_gal;/// Stores v…

主播三维能力总览

主播需要的三维能力包括话术能力、成交能力和表现力 主播的话术能力 主播在直播过程中运用语言和表达方式的能力。好的话术能力可以帮助主播吸引观众、增加互动、提高直播效果等。 1.了解观众需求:在直播前&#xff0c;主播应该了解观众的需求和兴趣&#xff0c;以便在直播过…

小程序商城免费搭建之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. 前端框架…