首页
/ Svelte 5 双模态运行机制:Legacy 与 Runes 模式的区别、源码判定逻辑与渐进式迁移指南

Svelte 5 双模态运行机制:Legacy 与 Runes 模式的区别、源码判定逻辑与渐进式迁移指南

2026-09-06 13:32:16作者:史锋燃Gardner

本文基于 Svelte 官方文档中的 Legacy 概述页,结合当前仓库编译器源码,解释 Svelte 5 中「legacy mode(遗留模式)」与「runes mode(符文模式)」的双轨运行机制:为什么 Svelte 3/4 的旧语法在 Svelte 5 中依然可用、编译器是如何判定一个组件处于哪种模式的、哪些旧 API 已被弃用但仍受支持,以及如何进行渐进式迁移。读完之后,你将能够准确判断项目中每个组件所处的响应式模式,并据此制定分阶段迁移策略。

为什么 Svelte 5 需要一份独立的 Legacy 文档

Svelte 5 对核心 API 做了一次显著升级,引入了三大新特性:

  • runes(符文):如 $state$derived$effect$props 等,见 what-are-runes
  • snippets(代码段):以 {@snippet} 语法取代了原来的 slot 传值方式,见 snippet 语法
  • event attributes(事件属性):用 onclick={handler} 直接作为属性,取代了 on:click 指令。

作为结果,Svelte 3/4 中的一批特性被标记为弃用(deprecated)——它们目前仍然可用(除非文档另有说明),但未来会被移除。官方文档明确建议:不要一次性重写,而是增量迁移(incrementally migrate)现有代码,具体路径参见 v5 迁移指南

Legacy 文档系列的受众非常清晰,正是两类人:

  1. 仍然在使用 Svelte 3/4 的开发者;
  2. 已经升级到 Svelte 5、但部分组件尚未迁移的开发者。

由于 Svelte 3/4 语法在 Svelte 5 中仍然工作,官方文档因此刻意区分了 legacy moderunes mode 两个概念。这是理解 Svelte 5 兼容机制的钥匙:组件一旦进入 runes mode(通过使用符文,或显式设置 runes: true 编译选项),legacy mode 的特性便不再可用

判定逻辑:编译器如何决定一个组件处于哪种模式

官方文档说「使用符文,或显式设置 runes: true 编译选项」即可进入 runes mode。这个判定并非简单的开关,而是有一套优先级链,可以从编译器源码中完整还原。

1. runes 选项的两种来源

runes 是一个可被覆盖的编译选项,默认值为 undefined(即自动检测),在 validate-options.js 中定义:

runes: parametric(() => /** @type {boolean | undefined} */ (undefined)),

它有两个来源,且在 compile() 入口 中被合并:

const combined_options = {
    ...validated,                       // 全局编译选项
    ...parsed_options,                  // <svelte:options> 中的组件级选项
    css: 'css' in parsed_options ? () => parsed_options.css ?? 'external' : validated.css,
    runes: 'runes' in parsed_options ? () => parsed_options.runes : validated.runes
};

即:组件内 <svelte:options runes={true|false}> 的声明优先级高于全局选项<svelte:options> 标签的解析逻辑在 1-parse/read/options.js

case 'runes': {
    component_options.runes = get_boolean_value(attribute);
    break;
}

这意味着你可以在单个组件内显式锁定模式——即使项目级配置设置了 runes: true,也能对个别遗留组件声明 runes={false} 强制保持 legacy 行为(源码中的注释「if they explicitly disabled runes, use the legacy behavior」印证了这一用途)。

2. 自动检测:未显式声明时的判定规则

runes 选项未显式给出时,2-analyze/index.js 执行自动检测:

const runes =
    runes_option ??
    (has_await || instance.has_await || Array.from(module.scope.references.keys()).some(is_rune));

从源码结构看,自动进入 runes mode 的触发条件是三者之一:

  • 组件存在顶层 awaithas_await);
  • 实例脚本中存在 awaitinstance.has_await,典型如 await onMount(...));
  • 模块作用域中引用了任意 rune 名称($state$derived 等)。

这与文档中「use runes 即进入 runes mode」的表述一致,并且解释了为什么顶层 await 只能在 runes 模式组件中使用——编译器会直接把这类组件划入 runes 阵营。

3. 边缘情况:maybe_runes 回退机制

组件级判定还有一个容易被忽略的分支。分析结果中记录了 maybe_runes 标志(2-analyze/index.js):

// if we are not in runes mode but we have no reserved references ($$props, $$restProps)
// and no `export let` we might be in a wannabe runes component that is using runes in an external
// module...we need to fallback to the runic behavior
maybe_runes: !runes && runes_option !== false && ...

可以推断:一个看似 legacy 的组件,如果既没有 export let 也没有 $$props/$$restProps 引用,它可能是在外部模块中使用 rune 的「准 runes 组件」(例如把 $state 封装在工具模块里)。对此类组件,编译器会回退到 runic 行为而不是套用 legacy 的响应式规则。这是双模态机制在「混合工程」中的关键容错设计。

4. 模式切换带来的编译差异

同一份源码在两种模式下并不只是语法可用性不同,编译器的默认行为也不同。从 analysis 对象构造 中可以看到两处直接由 runes 布尔值驱动的分支:

immutable: runes || options.immutable,
// ...
accessors: is_custom_element || (runes ? false : !!options.accessors) || ...
  • immutable:runes 模式下默认按不可变语义处理数据,而 legacy 模式只有在显式传入 immutable: true 时才如此;
  • accessorsaccessors 编译选项只影响 legacy 模式,runes 模式下直接为 false<svelte:options> 里出现 accessors/immutable 时还会触发告警,见 2-analyze/index.js 中的 analysis.runes 检查)。

这些细节说明:迁移不只是「换个写法」,还会改变编译器对属性访问器和数据可变性的默认假设。

弃用但仍在工作的 Legacy 特性清单

Legacy 文档目录(documentation/docs/99-legacy)逐条列出了在 Svelte 5 中被弃用、但当前仍受支持的 Svelte 3/4 特性。这是迁移时最需要对照的清单:

弃用特性 文档位置 Svelte 5 中的替代方案
响应式 let/var 顶层声明 01-legacy-let.md $state rune
基于赋值(count += 1)的响应式赋值 02-legacy-reactive-assignments.md $state 的引用追踪
export let 接收 props 03-legacy-export-let.md $props()
$$props / $$restProps 04-legacy-$$props-and-$$restProps.md $props() 解构与 rest
on: 事件指令 10-legacy-on.md 事件属性(onclick={...}
slots 插槽 20-legacy-slots.md snippets
$$slots 21-legacy-$$slots.md snippets
<svelte:fragment> 22-legacy-svelte-fragment.md snippet
<svelte:component> 30-legacy-svelte-component.md @render 标签
<svelte:self> 31-legacy-svelte-self.md 组件内直接引用自身
旧版组件 API($$ 内部、旧版 $set 等) 40-legacy-component-api.md 新组件 API

以响应式声明为例,01-legacy-let.md 展示了两种模式的语义差异:legacy 模式下,组件顶层变量自动被视为响应式,但响应式基于赋值触发,array.push() 这类方法调用本身不触发更新,需要一次「自赋值」(numbers = numbers)来通知编译器;runes 模式则用 $state 显式声明,配合代理实现,属性级别的深层变更也能被追踪。

双 AST 与 Legacy 运行时:兼容层的实现位置

兼容旧语法不止体现在模式判定上,编译器内部还维护着完整的 legacy 兼容层,迁移文档读者可以据此理解「Svelte 5 为什么还能编译 Svelte 3/4 代码」。

双 AST 输出compiler/legacy.js 的注释直白地写着「Transform our nice modern AST into the monstrosity emitted by Svelte 4」——它负责把现代 AST 转换回 Svelte 4 风格的 legacy AST,供仍依赖旧 AST 结构的第三方工具(如 linter、preprocessor)使用。配套的 parse 选项在 compiler/index.js 的 JSDoc 中给出了明确的废弃时间线:

  • modern 选项在 Svelte 5 中默认 false(即默认返回 legacy AST);
  • Svelte 6 中 modern 将默认变为 true
  • Svelte 7 中该选项将被移除

Legacy 运行时入口。仓库中保留了独立的 legacy 运行时模块 legacy-client.jslegacy-server.js,供旧版组件 API 使用;测试套件 tests/runtime-legacy 下约 2880 个测试文件持续回归验证 legacy 特性的行为。这说明「弃用但受支持」是有测试保障的承诺,而非口头声明。

实践建议:如何组织渐进式迁移

综合原文档与源码证据,一个混合 Svelte 3/4 与 Svelte 5 语法的代码库可以按以下方式组织:

  1. 按组件粒度迁移。每个组件的模式是独立判定的(显式选项 > 自动检测),因此可以在同一个项目中长期共存 legacy 组件与 runes 组件,逐文件替换,而不需要「一次性切换开关」。
  2. 新组件优先 runes。新写组件直接使用 $state/$props/snippet 语法即可自动进入 runes mode;若项目通过 <svelte:options runes={false}> 或全局选项显式关闭了 runes,注意新组件会意外落回 legacy 模式,迁移时应检查这一显式声明是否仍然必要。
  3. 警惕跨模式引用的边界。一个 runes 组件调用 legacy 组件时,props 传递与事件机制走的是 legacy 组件的旧 API;maybe_runes 机制处理了「外部模块使用 rune」的灰区,但不建议刻意制造这种模糊结构。
  4. 对照弃用清单逐项替换。按上文表格中的对应关系处理 on:、slot、export let$$props 等写法,每项的具体替换示例见对应 legacy 文档页。
  5. 完整的迁移步骤(包括 svelte migrate 迁移工具的用法)请参见 v5 迁移指南

另外,如果只关心 Svelte 3/4 语法本身而不涉及 Svelte 5,官方另行提供了对应版本的文档站点(v4 文档),其中保留了完整的旧版 API 参考;而本仓库的 Legacy 文档系列则是面向「仍在旧版本」与「正在过渡」两类读者的桥梁。

小结

Svelte 5 的兼容机制核心是双模态:legacy mode 保留 Svelte 3/4 的全部语法与语义(基于赋值/export let/slot/on: 指令),runes mode 提供全新的显式响应式 API;模式判定遵循「组件级 <svelte:options> > 全局编译选项 > 内容自动检测(rune 引用或顶层 await)」的优先级链,且模式不同会连带改变 immutableaccessors 等默认编译行为。Legacy 特性当前仍受支持并有测试保障,但随 AST modern 选项在 Svelte 6/7 的默认值变化可以看出明确的收敛方向——按照 v5 迁移指南 增量迁移,是让项目平稳跨越这一代际的正确路径。

登录后查看全文
热门项目推荐
相关项目推荐