菜单

组件生命周期

相关源文件

本文档解释了 Svelte 组件从初始化到销毁的生命周期,以及响应性如何与生命周期集成。有关响应性系统本身的信息,请参阅 响应性系统

概述

在 Svelte 中,组件在其生命周期中会经历几个阶段

  1. 创建 - 组件函数以 props 调用
  2. 挂载 - 组件被插入到 DOM 中
  3. 更新 - 组件响应状态更改
  4. 销毁 - 组件从 DOM 中移除

自 Svelte 5 起,组件生命周期主要通过 effect 系统进行管理,该系统通过细粒度的响应性处理初始渲染和后续更新。

来源:packages/svelte/src/internal/client/render.js72-265 packages/svelte/src/internal/client/reactivity/effects.js43-618 packages/svelte/src/internal/client/runtime.js44-865

组件创建

组件通过 `mount()` 函数创建和挂载,或通过 `hydrate()` 函数水合(附加到服务器渲染的 DOM)。两者最终都会调用内部的 `_mount()` 函数。

来源:packages/svelte/src/internal/client/render.js72-265 packages/svelte/src/internal/client/reactivity/effects.js229-266

组件创建过程

  1. 初始化:初始化 DOM 操作
  2. 事件设置:注册事件处理程序
  3. 根 effect 创建:使用 `component_root()` 创建一个根 effect
  4. 组件执行:执行组件函数
    • 组件上下文被推入堆栈
    • 组件函数以提供的 props 执行
    • 创建组件 DOM
    • 从堆栈中弹出上下文
  5. 注册:组件及其 destroy 函数存储在 WeakMap 中

组件函数返回一个组件实例对象,其中包含组件的任何导出值,这允许与组件进行编程交互。

来源:packages/svelte/src/internal/client/render.js168-265 packages/svelte/src/internal/client/reactivity/effects.js243-258

Effect 层次结构

组件由管理其生命周期和响应性的 effect 层次结构提供支持

  • ROOT_EFFECT:由 `component_root()` 创建,这是组件的顶级 effect
  • BRANCH_EFFECT:由 `branch()` 创建,表示组件树中的一个分支
  • RENDER_EFFECT:由 `render_effect()` 创建,处理模板渲染
  • EFFECT:由 `effect()` 创建,用于用户定义的 effect
  • BLOCK_EFFECT:由 `block()` 创建,处理模板块(if、each 等)

来源:packages/svelte/src/internal/client/reactivity/effects.js44-618 packages/svelte/src/internal/client/constants.js1-32

Effect 的创建与执行

Svelte 中的每个 effect 都有一个特定的创建过程和执行模式,构成了组件响应性的骨干

Effect 的创建

  1. `create_effect(type, fn, sync, push)` - 创建新 effect 的核心函数
  2. Effect 要么被推送到其父级(形成一个树),要么被调度执行
  3. 同步 effect 立即运行,异步 effect 通过微任务进行调度

Effect 的执行

  1. `update_effect(effect)` - 执行 effect 函数并捕获其返回值作为清理函数
  2. `update_reaction(reaction)` - 在执行期间跟踪依赖项
  3. `check_dirtiness(reaction)` - 检查 reaction 的依赖项是否已更改
  4. `execute_effect_teardown(effect)` - 如果存在,则运行清理函数

来源:packages/svelte/src/internal/client/reactivity/effects.js83-151 packages/svelte/src/internal/client/runtime.js160-497 packages/svelte/src/internal/client/runtime.js560-624

组件更新周期

当组件状态发生变化时,更新周期会管理 effect 的调度和执行方式

更新周期发生在

  1. 通过 `set()`、`update()` 或其他状态突变方法更改状态值时
  2. 这会将相关的 effect 标记为脏,并调度它们执行
  3. Effect 会被批处理并在微任务中执行以优化性能
  4. Effect 根据 effect 树以拓扑顺序执行
  5. DOM 更新是 effect 执行的结果

更新可以通过 `flushSync()` 同步强制执行,或者在下一个微任务后使用 `tick()` 执行。

来源:packages/svelte/src/internal/client/runtime.js743-865 packages/svelte/src/internal/client/reactivity/sources.js129-221

组件销毁

当组件不再需要时,调用 `unmount()` 来清理资源

销毁过程

  1. `unmount(component, options)` 获取组件的 destroy 函数
  2. 如果 `options.outro` 为 true,则过渡在移除前播放
  3. `destroy_effect()` 处理实际清理工作
    • 销毁子 effect
    • 移除 DOM 节点
    • 运行清理函数
    • 将 effect 与 effect 树取消链接

来源:packages/svelte/src/internal/client/render.js293-306 packages/svelte/src/internal/client/reactivity/effects.js426-480 packages/svelte/src/internal/client/reactivity/effects.js509-537

组件上下文

每个组件都有一个上下文对象来维护其状态

属性描述
p父上下文引用
c上下文值映射
d已销毁标志
e延迟 effect 数组
m已挂载标志
sProps (传统)
xExports (传统)
l传统生命周期属性

上下文尤其重要,用于

  • 维护组件层次结构
  • 提供 Context API 功能
  • 在传统模式下管理生命周期
  • 处理延迟 effect(在挂载后运行)

来源:packages/svelte/src/internal/client/types.d.ts12-66 packages/svelte/src/internal/client/reactivity/effects.js176-203

Runes 和传统模式下的生命周期

Svelte 5 使用 runes 进行生命周期管理,但保持与旧方法的兼容性

Runes 模式

传统模式

在这两种模式下,生命周期管理都通过 effect 系统进行,但使用了不同的 API。传统模式使用一组显式的生命周期函数,这些函数在编译期间转换为 effect。

来源: packages/svelte/src/internal/client/reactivity/effects.js175-218 packages/svelte/src/internal/client/reactivity/effects.js277-318 packages/svelte/src/internal/client/dom/legacy/lifecycle.js

注水过程

在服务器端渲染和随后的客户端水合过程中,组件遵循特殊的生命周期路径

水合过程

  1. 服务器使用特殊注释标记渲染 HTML
  2. 客户端在 DOM 中查找这些标记
  3. 组件在挂载时重用现有 DOM,而不是创建新的 DOM
  4. 如果检测到不匹配,DOM 将被销毁并从头开始重新创建

来源: packages/svelte/src/internal/client/render.js99-157 packages/svelte/src/internal/server/index.js62-113

处理组件块

特殊组件块(if、each、await 等)有自己的生命周期管理

If Blocks (如果块)

If 块创建分支效果,这些效果根据条件暂停/恢复

  • 当条件变为 false 时,块效果会暂停
  • 当条件变为 true 时,块效果会恢复或创建

Each Blocks (每个块)

Each 块有一个复杂的生命周期,包括

  • 旧项目和新项目的协调
  • 键值跟踪以实现高效更新
  • 通过过渡管理器支持动画

Await Blocks (等待块)

Await 块处理具有三种可能状态的 Promise

  • Pending (待定):等待 Promise 时的初始状态
  • Fulfilled (已完成):Promise 解析时
  • Rejected (已拒绝):Promise 被拒绝时

这些块中的每一个都会创建适当的效果来管理其生命周期,并确保在组件被销毁时进行正确的清理。

来源: packages/svelte/src/internal/client/dom/blocks/if.js packages/svelte/src/internal/client/dom/blocks/each.js42-609 packages/svelte/src/internal/client/dom/blocks/await.js

所有权和验证

在 Svelte 5 中,组件实现了所有权验证,以帮助防止常见错误

所有权验证系统确保

  1. 组件不直接修改其 props(来自父组件)
  2. 子组件只能绑定到其父组件拥有的状态,而不是通过父组件传递的 props
  3. 具有默认值的 Props 在使用可绑定 Props 时可以正确绑定

此系统有助于创建可预测的数据流并防止意外的副作用。

来源: packages/svelte/src/internal/client/dev/ownership.js10-66 packages/svelte/src/internal/client/proxy.js17-321

通用生命周期模式

初始化与清理

延迟效果

条件效果清理

这些模式利用组件生命周期和效果系统来确保正确的资源管理和清理。

来源: packages/svelte/src/internal/client/reactivity/effects.js175-218 packages/svelte/tests/signals/test.ts54-490

特殊生命周期注意事项

错误边界

组件可以充当错误边界,以捕获和处理其子组件中的错误

过渡管理

带有过渡的组件具有特殊的销毁过程

  1. 在被移除时,过渡会在实际 DOM 移除之前播放
  2. 如果在过渡期间组件被添加回来,则过渡会被反转

服务器端渲染生命周期

在 SSR 期间

  1. 组件函数执行以生成 HTML
  2. 没有实际的 DOM 操作发生
  3. 添加了特殊标记以进行水合
  4. onDestroy 回调在渲染后立即执行

来源: packages/svelte/src/internal/client/runtime.js234-358 packages/svelte/src/internal/client/reactivity/effects.js509-537 packages/svelte/src/internal/server/index.js58-113