Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • reactivity响应式

  • runtime-core运行时核心模块

    • watch和watcherEffect源码解析
    • scheduler调度器
    • directive自定义指令实现原理
    • provide依赖和inject注入详解
    • 生命周期函数Lifecycle解析
    • 渲染器renderer源码解析
    • patch方法详解
    • patch中组件的挂载解析
    • patch中组件的更新解析
      • 概览
      • 源码解析
        • updateComponent组件更新
        • 辅助方法
        • shouldUpdateComponent
        • hasPropsChanged
    • patch中DOM元素的挂载解析
    • patchDOM元素的更新解析
    • patch中的双端比较快速算法
  • runtime-dom运行时DOM模块

  • 5.18源码学习》
  • runtime-core运行时核心模块
东流
2025-09-23
目录

patch中组件的更新解析

# 概览

在processComponent处理组件方法中,若存在旧节点n1,则是调用updateComponent方法更新组件。本文会介绍updateComponent方法的全流程。

# 源码解析

# updateComponent组件更新

updateComponent方法接受三个参数:旧虚拟节点n1、新虚拟节点n2和是否需要优化optimized。该方法内部会先从节点n1中获取组件实例,并同时赋值给新节点n2的component,保证新旧节点共享同一个组件实例。然后调用shouldUpdateComponent方法判断是否需要更新组件;若需要更新,则继续判断组件实例上是否存在异步依赖并且实例尚未解析完成,若是,则调用updateComponentPreRender进行组件预渲染,然后返回;若不存在异步依赖或者存在依赖且实例已经解析完成,则将新节点n2赋值给实例的next属性,然后调用实例的update方法触发组件的重新渲染,该update方法实际上就是运行finishSetupComponent中的componentUpdateFn;若不需要更新组件,则新节点n2的DOM元素直接复用旧节点n1的DOM元素,并且将实例的vnode设置为n2。

const updateComponent = (n1, n2, optimized) => {
    const instance = n2.component = n1.component;
    if (shouldUpdateComponent(n1, n2, optimized)) {
        if (instance.asyncDep && !instance.asyncResolved) {
            updateComponentPreRender(instance, n2, optimized);
            return;
        } else {
            instance.next = n2;
            instance.update();
        }
    } else {
        n2.el = n1.el;
        instance.vnode = n2;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 辅助方法

updateComponent方法中主要用到shouldUpdateComponent和updateComponentPreRender两个辅助方法。关于updateComponentPreRender在组件的挂载中提过,该方法主要就是更新props和slots,以及调用flushPreFlushCbs处理前置队列中的任务。

# shouldUpdateComponent

shouldUpdateComponent方法会返回一个布尔值,表示是否需要更新组件。

function shouldUpdateComponent(prevVNode, nextVNode, optimized) {
  // 解构旧虚拟节点n1
  const { props: prevProps, children: prevChildren, component } = prevVNode;
  // 解构新虚拟节点n2
  const { props: nextProps, children: nextChildren, patchFlag } = nextVNode;
  
  const emits = component.emitsOptions;
  // 若新节点上有指令和过渡效果,则直接返回true,表示需要更新
  if (nextVNode.dirs || nextVNode.transition) {
    return true;
  }
  // 如果启用了优化模式,并且patchFlag存在
  if (optimized && patchFlag >= 0) {
    // 若是动态插槽,则返回true
    if (patchFlag & 1024) {
      return true;
    }
    // 若是全量props比较
    if (patchFlag & 16) {
      // 若旧节点上不存在属性,则判断新节点上是否存在属性,若存在则返回true,不存在,则返回false
      if (!prevProps) {
        return !!nextProps;
      }
      // 若旧节点上存在属性,则调用hasPropsChanged比较新旧props是否相等
      return hasPropsChanged(prevProps, nextProps, emits);
    } else if (patchFlag & 8) {
      // 若存在动态props,
      const dynamicProps = nextVNode.dynamicProps;
      // 则遍历动态props数组,若发现属性值变化且不是事件监听器,则返回true
      for (let i = 0; i < dynamicProps.length; i++) {
        const key = dynamicProps[i];
        if (nextProps[key] !== prevProps[key] && !isEmitListener(emits, key)) {
          return true;
        }
      }
    }
  } else {
  // 非优化模式处理
    // 若旧节点子节点或者新节点子节点存在 
    if (prevChildren || nextChildren) {
      // 则判断,若新节点子节点不存在或者新节点子节点不稳定,则返回true
      if (!nextChildren || !nextChildren.$stable) {
        return true;
      }
    }
    // 若新旧props相同,则返回false
    if (prevProps === nextProps) {
      return false;
    }
    // 若旧props不存在,则判断新props是否存在,若存在则需要更新;否则不需要更新
    if (!prevProps) {
      return !!nextProps;
    }
    // 若新props不存在,则返回true
    if (!nextProps) {
      return true;
    }
    // 否则调用hasPropsChanged比较新旧props是否相等
    return hasPropsChanged(prevProps, nextProps, emits);
  }
  // 默认返回false,表示不需要更新
  return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

shouldUpdateComponent方法会根据optimized采取不同的策略判断,若是优化模式,则根据新节点的patchFlag判断是否需要更新;若不是优化模式,则基于新旧节点的props/children进行比较,判断是否需要更新;

设计特点与优化策略

  1. PatchFlag优化:
  • 使用位运算快速判断更新类型
  • 减少不必要的全量比较
  • 动态Props只需检查特定属性
  1. 事件监听器优化
  • 跳过事件监听器属性的比较
  • 事件监听器变化不影响组件渲染
  1. 子节点稳定性检查

    • $stable标志避免不必要的子组件更新
    • 静态内容可标记为稳定
  2. 引用相等性短路

    • 相同引用直接跳过更新
  3. 动态插槽优化

    • 动态插槽变化需要更新组件
    • 静态插槽可跳过更新
  4. 过渡效果处理

    • 过渡组件需要特殊处理
    • 确保动画正确执行

# hasPropsChanged

hasPropsChanged方法用于新旧属性是否相等,若不等,则返回true,表示属性发生了改变。

function hasPropsChanged(prevProps, nextProps, emitsOptions) {
  const nextKeys = Object.keys(nextProps);
  // 先判断新旧属性的长度,即属性键的个数是否相等,若不等,则返回true
  if (nextKeys.length !== Object.keys(prevProps).length) {
    return true;
  }
  // 遍历旧属性的每个键,若该键的值与其在新属性的值不等,且不是事件监听器,则返回true,
  for (let i = 0; i < nextKeys.length; i++) {
    const key = nextKeys[i];
    if (nextProps[key] !== prevProps[key] && !isEmitListener(emitsOptions, key)) {
      return true;
    }
  }
  // 默认返回 false
  return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
编辑 (opens new window)
上次更新: 2025/09/25, 09:41:26
patch中组件的挂载解析
patch中DOM元素的挂载解析

← patch中组件的挂载解析 patch中DOM元素的挂载解析→

最近更新
01
patch中的双端比较快速算法
09-25
02
patchDOM元素的更新解析
09-25
03
patch中DOM元素的挂载解析
09-25
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式