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语言的基础上又搞了一套自己的关于类型转换的东西。 目…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
