vue3学习源码笔记(小白入门系列)------ 组件更新流程

news2024/9/21 11:13:45

目录

    • 说明
    • 例子
    • 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 = 1000
    const App = {
      components:{child},
      beforeMount() {
        console.log('beforeMount');
        
      },
      data() {
        return {
        }
      },
      setup() {
        const count = ref(1)

        const age = ref('20')


        onMounted(()=>{
          count.value = 5
          age.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
        }
   }
// render
        if (__DEV__) {
          startMeasure(instance, `render`)
        }
        // 新的vnode 
        const nextTree = renderComponentRoot(instance)
        if (__DEV__) {
          endMeasure(instance, `render`)
        }
        // 旧的vnode
        const prevTree = instance.subTree
        // 新的vnode 给下次渲染更新使用
        instance.subTree = nextTree

        if (__DEV__) {
          startMeasure(instance, `patch`)
        }
        // diff更新 
        patch(
          prevTree,
          nextTree,
          // parent may have changed if it's in a teleport
          hostParentNode(prevTree.el!)!,
          // anchor may have changed if it's in a fragment
          getNextHostNode(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 yet
        if (__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 避免子组件由于自身数据变化导致的重复更新
        invalidateJob(instance.update)
        // instance.update is the reactive effect.
        // 主动触发组件的更新
        instance.update()
      }
    } else {
      // no update needed. just copy over properties 不需要更新就把之前节点的元素 赋值给 新节点 在赋值到组件的vnode上
      n2.el = n1.el
      instance.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属性 也指向新的组件vnode
    instance.vnode = nextVNode
    // 清空next 属性 为下一次重新渲染做准备
    instance.next = null
    // 更新 props
    updateProps(instance, nextVNode.props, prevProps, optimized)
    // 更新 slots
    updateSlots(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

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

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

相关文章

MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布

一、MQTT介绍 1.1 什么是MQTT&#xff1f; MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的“轻量级”通讯协议&#xff0c;该协议构建于…

WhatsApp 时事通讯,又一个强大的营销功能

有兴趣通过时事通讯有效地接触您的受众吗&#xff1f;如果您的客户正在使用WhatsApp&#xff0c;使用WhatsApp商业通讯是理想的解决方案。在这篇文章中&#xff0c;我们将探讨使用 WhatsApp时事通讯进行客户沟通的优势。 什么是WhatsApp时事通讯&#xff1f; WhatsApp时事通讯…

vuex使用Cannot find module ‘./api/index.js‘

使用vuex中出现这个问题。我的vuex的配置。 import Vue from vue import Vuex from vuex import App from ./App.vue import store from "./store"; import router from ./router; import "/assets/css/base.css"; import VueRouter from vue-router; impo…

【LeetCode75】第四十二题 删除二叉搜索数中的节点

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一棵二叉搜索树&#xff0c;给我们一个目标值&#xff0c;让我们删除节点值等于目标值的节点&#xff0c;并且删除之后需要保持…

【原创】H3C路由器OSPF测试

网络拓扑图 路由器配置&#xff1a; 路由器1上接了4跟线&#xff0c;分别为这四个接口配置IP地址。 # interface GigabitEthernet0/0/0port link-mode routecombo enable copperip address 2.1.1.2 255.255.255.0 # interface GigabitEthernet0/0/1port link-mode routecombo…

独立站怎么做活动策划,独立站推广方式有哪些

独立站可以获得更多的用户关注和认可&#xff0c;进而实现业务增长和发展&#xff0c;因此活动策划至关重要&#xff0c;那么独立站怎么做活动策划&#xff0c;独立站推广方式有哪些&#xff1f; 独立站怎么做活动策划&#xff1f; 1、明确目标&#xff1a;在开始策划之前&am…

结构化日志记录增强网络安全性

日志是一种宝贵的资产&#xff0c;在监视和分析应用程序或组织的 IT 基础结构的整体安全状况和性能方面发挥着至关重要的作用。它们提供系统事件、用户活动、网络流量和应用程序行为的详细记录&#xff0c;从而深入了解潜在威胁或未经授权的访问尝试。虽然组织历来依赖于传统的…

ROS获取目标点导航完成状态(rospy)

文章目录 ROS获取目标点导航完成状态1. Action方式2. Topic方式3. 验证4. 状态码取值 ROS获取目标点导航完成状态 1. Action方式 在ROS中&#xff0c;导航框架默认使用move_base&#xff0c;所以对于导航状态的获取往往需要往move_base的交互状态组建上考虑。 一种常见的方法…

IP代理|一文看懂IPv4与IPv6

IP作为互联网的重要的桥梁&#xff0c;是计算机网络相互连接进行通信而设计的协议&#xff0c;正是因为有了P协议&#xff0c;因特网才得以迅速发展成为庞大的、开放的计算机通信网络。IP代理中常常可以看到IPv4与IPv6&#xff0c;今天就给各位跨境老板详细解释&#xff0c;他们…

手写Mybatis:第6章-数据源池化技术实现

文章目录 一、目标&#xff1a;数据源池化技术实现二、设计&#xff1a;数据源池化技术实现三、实现:数据源池化技术实现3.1 工程结构3.2 数据源池化技术关系图3.3 无池化链接实现3.4 有池化链接实现3.4.1 有连接的数据源3.4.2 池化链接的代理3.4.3 池状态定义3.4.4 pushConnec…

Viobot回环使用

Viobot回环是使用词袋匹配的方式&#xff0c;&#xff0c;当新的关键帧能够匹配词袋里面记录过的关键帧时&#xff0c;触发回环&#xff0c;将设备的当前位姿拉到历史位姿。 一.上位机操作 词袋使用方法 连接上设备&#xff0c;先停止算法。UI上点 设置 选到 loop 选项卡&…

【分布式搜索引擎elasticsearch】

文章目录 1.elasticsearch基础索引和映射索引库操作索引库操作总结 文档操作文档操作总结 RestAPIRestClient操作文档 1.elasticsearch基础 什么是elasticsearch&#xff1f; 一个开源的分布式搜索引擎&#xff0c;可以用来实现搜索、日志统计、分析、系统监控等功能 什么是…

python“梦寻”京东商品评论数据接口(含代码示例)

要通过京东的API获取商品详情评论数据&#xff0c;您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过京东开放平台API获取商品详情评论数据&#xff1a; 首先&#xff0c;确保您已注册成为京东开放平台的开发者&#xf…

微服务-gateway基本使用

文章目录 一、前言二、gateway网关1、什么是微服务网关&#xff1f;2、微服务架构下网关的重要性2.1、没有网关2.2、有网关 3、gateway的功能4、gateway实战4.1、依赖配置4.2、添加网关配置4.3、添加网关启动类4.4、查看项目是否启动成功4.5、验证路由配置是否正确 三、总结 一…

全网首发!大众宝来高尔夫polo领驭迈腾帕萨特奥迪A4A6B6B7等老车机增加带蓝牙控制的AUX解码模块,支持小程序原车按钮控制,支持外接高品质蓝牙模块

文章目录 前言1、设计指标2、PCB设计3、程序设计4、调试4.1蓝牙控制AUX解码板4.2自定义车机按钮控制其他高品质蓝牙音频模块4.3小程序使用 5、模块与车机连接方法6、结语 前言 ​ 之前写过四篇关于车机增加音频输入的方法。 1、07宝来经典车机CD收音机&#xff08;RC668&…

11.Redis数据库管理命令

Redis数据库管理命令 数据库管理selectdbsizeflushall / flushdb 数据库管理 redis 中的 database 是现成的&#xff0c;咱们用户不能创建新的数据库&#xff0c;也不能删除已有的数据库~ 默认 redis 给我们提供了 16 个数据库&#xff0c;名字为 数字0 到数字15 这16个数据库…

【深度学习】ChatGPT

本文基于Andrej Karpathy(OpenAI 联合创始人&#xff0c;曾担任特斯拉的人工智能和自动驾驶视觉主管)在Microsoft Build 2023上的演讲整理而成&#xff08;完整的视频在文末&#xff0c;直接拖到文章底部&#xff09;&#xff0c;主要分为2大部分&#xff1a; 1.如何训练GPT(可…

实战系列(一)| Dubbo和Spring Cloud的区别,包含代码详解

目录 1. 概述2. 核心功能3. 代码示例4. 适用场景 Dubbo 和 Spring Cloud 都是微服务架构中的重要框架&#xff0c;但它们的定位和关注点不同。Dubbo 是阿里巴巴开源的一个高性能、轻量级的 RPC 框架&#xff0c;主要用于构建微服务之间的服务治理。而 Spring Cloud 是基于 Spri…

3D开发工具HOOPS Publish如何快速创建交互式3D PDF文档?

HOOPS Publish是一款功能强大的SDK&#xff0c;可以创作丰富的工程数据并将模型文件导出为各种行业标准格式&#xff0c;包括PDF、STEP、JT和3MF。HOOPS Publish核心的3D数据模型是经过ISO认证的PRC格式(ISO 14739-1:2014)&#xff0c;它为装配树、拓扑和几何、产品制造信息和视…

基于grpc从零开始搭建一个准生产分布式应用(6) - 02 - MapStruct数据转换

一、基础转换 1.1、基础类型 基本类型、包装类、BigDecimal转String默认使用DecimalFormat格式化&#xff0c;Mapping#numberFormat可以指定格式&#xff0c;Date转String默认使用SimpleDateFormat格式化&#xff0c;如默认格式不符要求&#xff0c;可以用&#xff0c;Mapping…