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语言的基础上又搞了一套自己的关于类型转换的东西。 目…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
