vue3学习源码笔记(小白入门系列)------ 组件更新流程
目录
- 说明
- 例子
- processComponent
- componentUpdateFn
- updateComponent
- updateComponentPreRender
- 总结
说明
由于响应式相关内容太多,决定先接着上文组件挂载后,继续分析组件后续更新流程,先不分析组件是如何分析的。
例子
将这个 用例 使用 vitest 插件 debug 运行起来 慢慢配合下面 核心代码 来理解
it('should support runtime template compilation', async () => {const container = document.createElement('div')container.classList.add('app')const foo = {name:'kd'}let temp ;// 子组件const child = defineComponent({template: `<div><p>{{age}}</p></div>`,props:{age:{type: Number,default:20}}})let num = 1000const App = {components:{child},beforeMount() {console.log('beforeMount');},data() {return {}},setup() {const count = ref(1)const age = ref('20')onMounted(()=>{count.value = 5age.value = '2'})onUpdated(()=>{num++})// const obj = reactive({name:'kd'})// return {obj,time}return ()=>{return h('div',[count.value,h(child,{age:age.value})])}}}createApp(App).mount(container)await nextTick()// time.value = 2000// await nextTick()expect(foo).equals(temp)expect(container.innerHTML).toBe(`0`)})
processComponent
还记得 patch 中 processComponent 初始化副作用函数中 的 updateComponentFn 吗?
当 onMounted 中 count age 响应式数据改变时 就会触发 App 组件 instance 中的 effect (也就是 app 组件在初始化挂载时候创建的)
// packages/runtime-core/src/renderer.ts
const setupRenderEffect: SetupRenderEffectFn = (instance,initialVNode,container,anchor,parentSuspense,isSVG,optimized) => {const componentUpdateFn = ()=>{...}
const effect = (instance.effect = new ReactiveEffect({componentUpdateFn,() => queueJob(update),instance.scope}))const update: SchedulerJob = (instance.update = () => effect.run())update.id = instance.uid//... 省略部分逻辑update()
}
其中 effect 就是 响应式数据更新 会触发调用的 就会走到 componentUpdateFn 中的组件更新部分
componentUpdateFn
const componentUpdateFn = ()=>{if (!instance.isMounted) {...}else {// 组件更新// updateComponent// This is triggered by mutation of component's own state (next: null) 由组件自身状态的突变触发时(next: null)// OR parent calling processComponent (next: VNode) 父组件 调用一般就是 有新的属性 props slots 改变 有新的vnode let { next, bu, u, parent, vnode } = instance// 如果有 next 的话说明需要更新组件的数组(props, slot 等)let originNext = next// ... 省略if (next) {next.el = vnode.el// 更新组件vnode实例信息 props slots 等updateComponentPreRender(instance, next, optimized)} else {//没有代表 不需要更新 自身next = vnode}}
// renderif (__DEV__) {startMeasure(instance, `render`)}// 新的vnode const nextTree = renderComponentRoot(instance)if (__DEV__) {endMeasure(instance, `render`)}// 旧的vnodeconst prevTree = instance.subTree// 新的vnode 给下次渲染更新使用instance.subTree = nextTreeif (__DEV__) {startMeasure(instance, `patch`)}// diff更新 patch(prevTree,nextTree,// parent may have changed if it's in a teleporthostParentNode(prevTree.el!)!,// anchor may have changed if it's in a fragmentgetNextHostNode(prevTree),instance,parentSuspense,isSVG)if (__DEV__) {endMeasure(instance, `patch`)}next.el = nextTree.el}
这时候 的 instance 是app 由于是内部数据触发的渲染,所以本身的 props slots 并没有发生改变 所以 这时候 next 为null (后面再说明什么时候 执行 updateComponentPreRender)
走到下面 patch 后 会更新 child 组件 这时候 又会进入 processComponent 会走到 updateComponent 方法
updateComponent
const updateComponent = (n1: VNode, n2: VNode, optimized: boolean) => {const instance = (n2.component = n1.component)!// 先去判断组件自身是否需要被更新 if (shouldUpdateComponent(n1, n2, optimized)) {if (__FEATURE_SUSPENSE__ &&instance.asyncDep &&!instance.asyncResolved) {// async & still pending - just update props and slots// since the component's reactive effect for render isn't set-up yetif (__DEV__) {pushWarningContext(n2)}updateComponentPreRender(instance, n2, optimized)if (__DEV__) {popWarningContext()}return} else {// normal update 将 需要instance.next = n2// in case the child component is also queued, remove it to avoid// double updating the same child component in the same flush.// 先执行 invalidataJob 避免子组件(指的是app 的 子组件child)由于自身数据变化导致的重复更新 去除queue 中 子组件的更新 任务(就是子组件child自身的 update)invalidateJob(instance.update)// instance.update is the reactive effect.// 主动触发child组件的更新instance.update()}} else {// no update needed. just copy over properties 不需要更新就把之前节点的元素 赋值给 新节点 在赋值到组件的vnode上n2.el = n1.elinstance.vnode = n2}}
这时候 child 组件实例 instance next 属性 会被复制 成 新的vnode 在手动触发组件更新 又走到 child instance 实例初始化 生成的 componentUpdateFn 中 这时候 就会 走有 next 逻辑 会去更新 child 组件的 props slots 等属性
再来看看 updateComponentPreRender
updateComponentPreRender
const updateComponentPreRender = (instance: ComponentInternalInstance,nextVNode: VNode,optimized: boolean) => {// 新组件 vnode 的 component 属性指向组件实例nextVNode.component = instance// 旧组件vnode 的 props属性const prevProps = instance.vnode.props//组件实例的vnode属性 也指向新的组件vnodeinstance.vnode = nextVNode// 清空next 属性 为下一次重新渲染做准备instance.next = null// 更新 propsupdateProps(instance, nextVNode.props, prevProps, optimized)// 更新 slotsupdateSlots(instance, nextVNode.children, optimized)pauseTracking()// props update may have triggered pre-flush watchers.// flush them before the render update.flushPreFlushCbs()resetTracking()}
child 更新完 自身属性后 执行renderComponentRoot 根据新的组件属性 生成新的 vnode 再会 走 patch = > processElement => 再 diff 更新…
普通元素的比较规则 就不展开说了


总结
processComponent 处理组件 vnode 本质就是先去判断子组件是否需要更新。
如果需要 则 递归子组件的副作用渲染函数来更新,否则仅仅更新一些vnode的属性,并让子组件 实例保留 对组件(自身) vnode 的引用,用于子组件自身数据变化引起组件(自身)重新渲染的时候可以拿到最新的组件(自身)vnode
相关文章:
vue3学习源码笔记(小白入门系列)------ 组件更新流程
目录 说明例子processComponentcomponentUpdateFnupdateComponentupdateComponentPreRender 总结 说明 由于响应式相关内容太多,决定先接着上文组件挂载后,继续分析组件后续更新流程,先不分析组件是如何分析的。 例子 将这个 用例 使用 vi…...
数学建模B多波束测线问题B
数学建模多波束测线问题 1.问题重述: 单波束测深是一种利用声波在水中传播的技术来测量水深的方法。它通过测量从船上发送声波到声波返回所用的时间来计算水深。然而,由于它是在单一点上连续测量的,因此数据在航迹上非常密集,但…...
Pytest 框架执行用例流程浅谈
背景: 根据以下简单的代码示例,我们将从源码的角度分析其中的关键加载执行步骤,对pytest整体流程架构有个初步学习。 代码示例: import pytest def test_add(): assert 1 1 2 def test_sub(): assert 2 - 1 1 通过 pytes…...
C#__资源访问冲突和死锁问题
/// 线程的资源访问冲突:多个线程同时申请一个资源,造成读写错乱。 /// 解决方案:上锁,lock{执行的程序段}:同一时刻,只允许一个线程访问该程序段。 /// 死锁问题: /// 程序中的锁过多…...
机器学习——Logistic Regression
0、前言: Logistic回归是解决分类问题的一种重要的机器学习算法模型 1、基本原理: Logistic Regression 首先是针对二分类任务提出的一种分类方法如果将概率看成一个数值属性,则二元分类问题的概率预测就可以转化为一个回归问题。这种思路最…...
创建husky规范前端项目
创建husky规范前端项目 .husky文件是一个配置文件,用于配置Git钩子。Git钩子是在Git操作时触发的脚本,可以用于自动化一些任务,比如代码格式化、代码检查、测试等。.husky文件可以指定在Git的不同操作(如commit、push等ÿ…...
深浅拷贝与赋值
数据类型 数据类型 在JavaScript中,数据类型有两大类。一类是基本数据类型,一类是引用数据类型。 基本数据类型有六种:number、string、boolean、null、undefined、symbol。 基本数据类型存放在栈中。存放在栈中的数据具有数据大小确定&a…...
bert ranking pairwise demo
下面是用bert 训练pairwise rank 的 demo import torch from torch.utils.data import DataLoader, Dataset from transformers import BertModel, BertTokenizer from sklearn.metrics import pairwise_distances_argmin_minclass PairwiseRankingDataset(Dataset):def __ini…...
GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图
GPT对于每个科研人员已经成为不可或缺的辅助工具,不同的研究领域和项目具有不同的需求。例如在科研编程、绘图领域:1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATLAB还是其他语言,都可以为你提供相关的代码示例。2、数据可视…...
SpringBoot整合Swagger3
前言 swagger是啥,是干什么的,有什么用,我想在这里我就不用介绍了,下面直接代码演示。 添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0…...
detectron2 install path
>>> import detectron2 >>> detectron2_path detectron2.__file__ >>> print(detectron2.__file__)...
如何将DHTMLX Suite集成到Scheduler Lightbox中?让项目管理更可控!
在构建JavaScript调度器时,通常需要为最终用户提供一个他们喜欢的方式来计划事件,这是Web开发人员喜欢认可DHTMLX Scheduler的重要原因,它在这方面提供了完全的操作自由,它带有lightbox弹出窗口,允许通过各种控件动态更…...
什么是JVM常用调优策略?分别有哪些?
目录 一、JVM调优 二、堆内存大小调整 三、垃圾回收器调优 四、线程池调优 一、JVM调优 Java虚拟机(JVM)的调优主要是为了提高应用程序的性能,包括提高应用程序的响应速度和吞吐量。以下是一些常用的JVM调优策略: 堆内存大小…...
《向量数据库指南》——向量数据库Milvus Cloud 2.3的可运维性:从理论到实践
一、引言 在数据科学的大家庭中,向量数据库扮演着重要角色。它们通过独特的向量运算机制,为复杂的机器学习任务提供了高效的数据处理能力。然而,如何让这些数据库在生产环境中稳定运行,成为了运维团队的重要挑战。本文将深入探讨向量数据库的可运维性,并分享一些有趣的案…...
select多选回显问题 (取巧~)
要实现的效果: 实际上select选择框,我想要的是数组对象,但是后端返回来的是个字符串。 以下是解决方法: 以上是一种简单的解决方法~ 也可以自己处理数据或者让后端直接改成想要的格式。...
光伏并网双向计量表ADL400
安科瑞 华楠 ADL400 导轨式多功能电能表,是主要针对电力系统,工矿企业,公用设施的电能统计、 管理需求而设计的一款智能仪表,产品具有精度高、体积小、安装方便等优点。集成常见电 力参数测量及电能计量及考核管理,…...
十三、MySQL(DQL)语句执行顺序
1、DQL语句执行顺序: (1)from来决定表 # where来指定查询的条件 (2)group by指定分组 # having指定分组之后的条件 (3)select查询要返回哪些字段 (4)order by根据字段内容&#…...
【高德地图】根据经纬度多边形的绘制(可绘制区域以及任意图形)
官方示例 https://lbs.amap.com/demo/jsapi-v2/example/overlayers/polygon-draw <!doctype html> <html> <head><meta charset"utf-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name&quo…...
C++ std::pair and std::list \ std::array
std::pair<第一个数据类型, 第二个数据类型> 变量名 例如: std::pair<int, string> myPair; myPair.first;拿到第一个int变量 myPair.second拿到第二个string变量 std::pair需要引入库#include "utility" std::make_pair() 功能制作一个…...
C++的类型转换
前言 我们都知道C是兼容C语言的在C语言中存在两种方式的类型转换,分别是隐式类型转换和显示类型转换(强制类型转换),但是C觉得C语言的这套东西是够好,所以在兼容C语言的基础上又搞了一套自己的关于类型转换的东西。 目…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...
网页端 js 读取发票里的二维码信息(图片和PDF格式)
起因 为了实现在报销流程中,发票不能重用的限制,发票上传后,希望能读出发票号,并记录发票号已用,下次不再可用于报销。 基于上面的需求,研究了OCR 的方式和读PDF的方式,实际是可行的ÿ…...
STL 2迭代器
文章目录 1.迭代器2.输入迭代器3.输出迭代器1.插入迭代器 4.前向迭代器5.双向迭代器6.随机访问迭代器7.不同容器返回的迭代器类型1.输入 / 输出迭代器2.前向迭代器3.双向迭代器4.随机访问迭代器5.特殊迭代器适配器6.为什么 unordered_set 只提供前向迭代器? 1.迭代器…...
