基于 Svelte 5 源码深度解析
2026-03-10 | 技术深度解读
第一部分:基础篇
第二部分:核心数据结构
第三部分:核心机制
第四部分:实战应用
Svelte — 编译时前端框架,将组件编译为高效的命令式 JavaScript
┌─────────────────────────────────────────┐
│ Svelte 编译器 │
├─────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌────────┐│
│ │ Parse │→│ Analyze │→│Transform││
│ │ 解析 │ │ 分析 │ │ 转换 ││
│ └──────────┘ └──────────┘ └────────┘│
│ ↓ ↓ ↓ │
│ AST + Scope Analysis JS Code │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │Preprocess│ │ CSS │ │
│ │ 预处理 │ │ 样式 │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────┘
Phase 1: Parse — 源码 → AST
Phase 2: Analyze — 作用域、依赖、响应式分析
Phase 3: Transform — AST → 可执行 JavaScript
// 编译器主入口
export function compile(source, options) {
source = remove_bom(source);
state.reset({ warning: options.warningFilter, ... });
const validated = validate_component_options(options, '');
let parsed = _parse(source); // Phase 1
const analysis = analyze_component(parsed, source, combined_options); // Phase 2
const result = transform_component(analysis, source, combined_options); // Phase 3
result.ast = to_public_ast(source, parsed, options.modernAst);
return result;
}
// Svelte 4 - 响应式声明
let count = 0;
$: doubled = count * 2;
// Svelte 5 - Runes
let count = $state(0);
let doubled = $derived(count * 2);
$effect(() => {
console.log('count changed:', count);
});
$state — 响应式状态
$derived — 派生状态
$effect — 副作用
$props — 组件属性
| 版本 | 发布时间 | 关键特性 |
|---|---|---|
| Svelte 1 | 2016 | 初始版本,编译时框架概念 |
| Svelte 2 | 2018 | 响应式声明、组件 API |
| Svelte 3 | 2019 | 响应式语法糖、Store |
| Svelte 4 | 2023 | 性能优化、TypeScript 改进 |
| Svelte 5 | 2024 | Runes、细粒度响应式 |
Svelte 4(Legacy)
<script>
let count = 0;
$: doubled = count * 2;
function increment() {
count += 1;
}
</script>
Svelte 5(Runes)
<script>
let count = $state(0);
let doubled = $derived(count * 2);
function increment() {
count += 1;
}
</script>
Runes 模式提供更精确的依赖追踪和更好的类型推断
AST — 抽象语法树
Scope — 作用域
Binding — 变量绑定
Analysis — 编译分析结果
// 编译分析结果结构
interface ComponentAnalysis {
name: string;
runes: boolean; // 是否使用 Runes 模式
module: Js; // module script
instance: Js; // instance script
template: Template; // 模板 AST
exports: Export[]; // 导出信息
reactive_statements: Map; // 响应式语句
css: CssAnalysis; // CSS 分析
}
// Svelte AST 节点类型(部分)
type TemplateNode =
| RegularElement // <div>
| Component // <MyComponent>
| IfBlock // {#if}
| EachBlock // {#each}
| AwaitBlock // {#await}
| KeyBlock // {#key}
| SnippetBlock // {#snippet}
| Text // 文本
| ExpressionTag // {expression}
| Attribute // 属性
| BindDirective // bind:
| OnDirective // on:
| ClassDirective // class:
| StyleDirective // style:
export class Parser {
readonly template: string; // 源码
loose: boolean; // 宽松模式
index = 0; // 当前位置
ts = false; // TypeScript 模式
stack: AST.TemplateNode[] = []; // 节点栈
fragments: AST.Fragment[] = []; // 片段栈
root: AST.Root; // AST 根节点
meta_tags: Record<string, boolean> = {};
constructor(template, loose) {
this.template = template.trimEnd();
// 检测 lang="ts"
this.ts = match_lang?.[2] === 'ts';
this.root = { type: 'Root', ... };
// 状态机解析
while (this.index < this.template.length) {
state = state(this) || fragment;
}
}
}
class Parser {
// 匹配字符串
match(str) {
return this.template.startsWith(str, this.index);
}
// 消费字符串
eat(str, required = false) {
if (this.match(str)) {
this.index += str.length;
return true;
}
if (required) e.expected_token(this.index, str);
return false;
}
// 读取标识符
read_identifier() {
const start = this.index;
// 使用 acorn 的 isIdentifierStart/Char
// 返回 Identifier 节点
}
// 跳过空白
allow_whitespace() {
while (is_whitespace(this.template.charCodeAt(this.index))) {
this.index++;
}
}
}
class Scope {
parent: Scope | null;
declarations: Map<string, Binding>;
references: Map<string, Reference[]>;
// 声明变量
declare(id, kind, declaration_kind) {
const binding = {
kind, // 'normal' | 'state' | 'prop' | ...
declaration_kind, // 'let' | 'const' | 'var' | 'import'
node: id,
references: [],
reassigned: false,
mutated: false
};
this.declarations.set(id.name, binding);
return binding;
}
// 获取绑定
get(name) {
return this.declarations.get(name) ??
this.parent?.get(name);
}
}
| 类型 | 说明 | 示例 |
|---|---|---|
| normal | 普通变量 | let x = 1 |
| state | 响应式状态 | let x = $state(0) |
| derived | 派生状态 | let x = $derived(y) |
| prop | 组件属性 | let { x } = $props() |
| bindable_prop | 可绑定属性 | export let x |
| store_sub | Store 订阅 | $store |
const analysis = {
name: 'MyComponent',
root: scope_root,
module: { ast, scope, scopes, has_await },
instance: { ast, scope, scopes, has_await },
template: { ast, scope, scopes },
runes: true, // Runes 模式
immutable: true, // 不可变数据
exports: [], // 导出信息
reactive_statements: new Map(),
binding_groups: new Map(),
slot_names: new Map(),
css: {
ast: root.css,
hash: 'svelte-xxx',
keyframes: []
},
snippet_renderers: new Map(),
async_deriveds: new Set()
};
interface ComponentAnalysis extends Analysis {
// 响应式状态
reactive_statements: Map<LabeledStatement, ReactiveStatement>;
// 绑定组(用于 bind:group)
binding_groups: Map<string, BindingGroup>;
// 插槽
slot_names: Map<string, AST.SlotElement>;
// 使用标记
uses_props: boolean;
uses_rest_props: boolean;
uses_slots: boolean;
uses_component_bindings: boolean;
uses_render_tags: boolean;
// 需求标记
needs_context: boolean;
needs_props: boolean;
needs_mutation_validation: boolean;
}
const visitors = {
_(node, { state, next, path }) {
// 通用处理:提取注释、作用域切换
const scope = state.scopes.get(node);
next(scope !== undefined ? { ...state, scope } : state);
},
Identifier(node, context) { /* 标识符处理 */ },
CallExpression(node, context) { /* 函数调用处理 */ },
EachBlock(node, context) { /* each 块处理 */ },
IfBlock(node, context) { /* if 块处理 */ },
// ... 更多访问者
};
// 使用 zimmerframe 进行遍历
walk(ast, state, visitors);
Runes 是 Svelte 5 的响应式原语,以 $ 开头
// 基础用法
let count = $state(0);
let name = $state('Svelte');
// 对象和数组
let items = $state([1, 2, 3]);
let user = $state({ name: 'Chuck' });
// 深层响应式
let data = $state({
nested: {
value: 42
}
});
// data.nested.value 自动响应式
// 类字段
class Counter {
count = $state(0);
increment = () => this.count++;
}
// 简单派生
let count = $state(0);
let doubled = $derived(count * 2);
// 复杂计算
let filtered = $derived(
items.filter(item => item.active)
);
// $derived.by 用于复杂逻辑
let total = $derived.by(() => {
let sum = 0;
for (const item of items) {
sum += item.price;
}
return sum;
});
// 编译后
// let doubled = $.derived(() => count * 2);
// 基础副作用
$effect(() => {
console.log('count changed:', count);
});
// 清理函数
$effect(() => {
const timer = setInterval(() => {
console.log('tick');
}, 1000);
return () => clearInterval(timer);
});
// 依赖控制
$effect(() => {
// 只在 count 变化时执行
// 即使 name 变化也不会重新执行
console.log(count);
}, [count]);
export function compile(source, options) {
// 1. 移除 BOM
source = remove_bom(source);
// 2. 重置状态
state.reset({
warning: options.warningFilter,
filename: options.filename
});
// 3. 验证选项
const validated = validate_component_options(options, '');
// 4. 解析
let parsed = _parse(source);
// 5. 处理 TypeScript
if (parsed.metadata.ts) {
parsed = {
...parsed,
fragment: remove_typescript_nodes(parsed.fragment),
// ...
};
}
// 6. 分析 + 转换
const analysis = analyze_component(parsed, source, combined_options);
const result = transform_component(analysis, source, combined_options);
return result;
}
export function parse(template, { modern, loose } = {}) {
source = remove_bom(source);
state.set_source(template);
const parser = new Parser(template, loose);
return parser.root;
}
// Parser 构造函数中的状态机
constructor(template, loose) {
// ...
let state = fragment;
while (this.index < this.template.length) {
state = state(this) || fragment;
}
// 验证标签闭合
if (this.stack.length > 1) {
e.element_unclosed(current, current.name);
}
}
export function analyze_component(root, source, options) {
const scope_root = new ScopeRoot();
// 1. 创建作用域
const module = js(root.module, scope_root, false, null);
const instance = js(root.instance, scope_root, true, module.scope);
const template = create_template_scope(root.fragment, scope_root, instance.scope);
// 2. 检测 Runes 模式
const runes = options.runes ??
(has_await || instance.has_await ||
Array.from(module.scope.references.keys()).some(is_rune));
// 3. 创建分析对象
const analysis = {
name, module, instance, template,
runes, exports: [], reactive_statements: new Map(),
// ...
};
// 4. 遍历 AST
for (const { ast, scope, scopes } of [module, instance, template]) {
walk(ast, { scope, scopes, analysis, ... }, visitors);
}
// 5. CSS 分析
if (analysis.css.ast) {
analyze_css(analysis.css.ast, analysis);
prune(analysis.css.ast, analysis.elements);
}
return analysis;
}
export function transform_component(analysis, source, options) {
if (options.generate === false) {
return { js: null, css: null, warnings, metadata, ast };
}
// 根据目标选择转换器
const program = options.generate === 'server'
? server_component(analysis, options)
: client_component(analysis, options);
// 生成代码
const js = print(program, ts({ comments: analysis.comments }), {
sourceMapContent: source,
sourceMapSource: js_source_name
});
// 生成 CSS
const css = analysis.css.ast && !analysis.inject_styles
? render_stylesheet(source, analysis, options)
: null;
return { js, css, warnings, metadata, ast };
}
export default async function preprocess(source, preprocessor, options) {
const filename = options?.filename;
const preprocessors = Array.isArray(preprocessor)
? preprocessor
: [preprocessor];
const result = new PreprocessResult(source, filename);
// 依次应用预处理器
for (const preprocessor of preprocessors) {
// markup 预处理
if (preprocessor.markup) {
result.update_source(
await process_markup(preprocessor.markup, result)
);
}
// script 预处理
if (preprocessor.script) {
result.update_source(
await process_tag('script', preprocessor.script, result)
);
}
// style 预处理
if (preprocessor.style) {
result.update_source(
await process_tag('style', preprocessor.style, result)
);
}
}
return result.to_processed();
}
// CSS 处理流程
if (analysis.css.ast) {
// 1. 分析 CSS
analyze_css(analysis.css.ast, analysis);
// 2. 修剪未使用的样式
prune(analysis.css.ast, analysis.elements);
// 3. 警告未使用的选择器
if (!should_ignore_unused) {
warn_unused(analysis.css.ast);
}
}
// CSS Hash 生成
const css = {
ast: root.css,
hash: options.cssHash({
css: root.css.content.styles,
filename: state.filename,
name: component_name,
hash
}),
keyframes: [],
has_global: false
};
// 选项: generate: 'server'
const program = server_component(analysis, options);
// 服务端编译特点
// 1. 生成 render 函数
// 2. 无需客户端运行时
// 3. 直接输出 HTML 字符串
// 示例输出
function render(_result, _props, _bindings, _slots, _context) {
let count = _props.count ?? 0;
return `<div>
<button>Count: ${count}</button>
</div>`;
}
// 选项: generate: 'client'(默认)
const program = client_component(analysis, options);
// 客户端编译特点
// 1. 生成组件类
// 2. 包含响应式逻辑
// 3. 事件处理和 DOM 更新
// 示例输出
class MyComponent extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment,
safe_not_equal, { count: 0 });
}
get count() { return this.$$.ctx[0]; }
set count(value) { this.$set({ count: value }); }
}
// Svelte 4 响应式语句
$: {
if (count > 10) {
console.log('count is large');
}
}
// 分析流程
// 1. 检测 LabeledStatement(label.name === '$')
// 2. 提取依赖
// 3. 创建 ReactiveStatement
// 4. 拓扑排序
function order_reactive_statements(unsorted) {
// 检测循环依赖
const cycle = check_graph_for_cycles(edges);
if (cycle?.length) {
e.reactive_declaration_cycle(declaration, cycle);
}
// 拓扑排序
const reactive_declarations = new Map();
// ...
}
// Store 自动订阅
import { writable } from 'svelte/store';
const count = writable(0);
// $ 前缀自动订阅
console.log($count); // 自动订阅并获取值
// 编译器处理
// 1. 检测 $ 前缀标识符
// 2. 创建 store_sub 绑定
// 3. 生成订阅/取消订阅代码
// 编译后
let $count;
const unsubscribe = count.subscribe(value => {
$count = value;
});
onDestroy(unsubscribe);
// 定义 Snippet
{#snippet figure(image)}
<figure>
<img src={image.src} alt={image.caption} />
<figcaption>{image.caption}</figcaption>
</figure>
{/snippet}
// 使用 Snippet
{#each images as image}
{figure(image)}
{/each}
// 传递给子组件
<Gallery {figure} />
// 编译为函数
const figure = (image) => {
return figure_element(image);
};
编译器模式
运行时模式
编译器大量使用访问者模式处理不同类型的 AST 节点
// 定义访问者
const visitors = {
Identifier(node, context) {
const binding = context.state.scope.get(node.name);
if (binding) {
binding.references.push({ node, path: context.path });
}
},
CallExpression(node, context) {
const rune = get_rune(node, context.state.scope);
if (rune === '$state') {
// 处理 $state() 调用
}
},
// ... 更多访问者
};
// 遍历
walk(ast, state, visitors);
class Scope {
parent: Scope | null;
declarations: Map<string, Binding>;
references: Map<string, Reference[]>;
// 查找变量
get(name: string): Binding | null {
if (this.declarations.has(name)) {
return this.declarations.get(name);
}
return this.parent?.get(name) ?? null;
}
// 生成唯一名称
generate(name: string): string {
let i = 1;
let candidate = name;
while (this.declarations.has(candidate)) {
candidate = `${name}_${i++}`;
}
return candidate;
}
}
// 计算依赖图
function trace_references(node, reads, writes, scope) {
walk(node, { scope }, {
Identifier(node, context) {
const parent = context.path.at(-1);
if (is_reference(node, parent)) {
const binding = context.state.scope.get(node.name);
if (binding) {
reads.add(binding);
}
}
},
AssignmentExpression(node, context) {
// 追踪写入
for (const pattern of unwrap_pattern(node.left)) {
const binding = scope.get(pattern.name);
if (binding) writes.add(binding);
}
}
});
}
源码 (.svelte)
↓
┌────────────┐
│ Preprocess │ ← TypeScript/SCSS 预处理
└────────────┘
↓
┌────────────┐
│ Parse │ → AST (Root/Fragment/Nodes)
└────────────┘
↓
┌────────────┐
│ Analyze │ → Scope/Binding/Analysis
│ │ - 作用域创建
│ │ - 依赖追踪
│ │ - 响应式检测
└────────────┘
↓
┌────────────┐
│ Transform │ → JavaScript AST
│ │ - 服务端/客户端
│ │ - 代码优化
└────────────┘
↓
┌────────────┐
│ Print │ → JS Code + SourceMap
└────────────┘
↓
编译结果 { js, css, ast, warnings }
// 使用 zimmerframe 遍历
import { walk } from 'zimmerframe';
walk(ast, state, {
// 通用处理(每个节点)
_(node, { next, path }) {
const scope = scopes.get(node);
next(scope ? { ...state, scope } : state);
},
// 特定节点处理
EachBlock(node, context) {
// 处理 each 块
// 创建 each 作用域
// 追踪绑定
},
Identifier(node, context) {
// 处理标识符引用
// 记录到 binding.references
}
});
编译时优化
运行时优化
Svelte 无虚拟 DOM,直接操作真实 DOM
// Svelte 组件天然支持 Tree Shaking
// 未使用的代码会被移除
// 示例:条件编译
<script>
const DEV = import.meta.env.DEV;
function debug() {
if (DEV) {
console.log('debug info');
}
}
</script>
// 生产构建时,DEV = false
// console.log 语句会被移除
性能
$derived 替代计算属性key 优化 each$effect可维护性
// ❌ 错误:在 $effect 中修改依赖
$effect(() => {
count++; // 无限循环!
});
// ✅ 正确:使用事件处理
function increment() {
count++;
}
// ❌ 错误:混用 Legacy 和 Runes
let x = $state(0);
$: doubled = x * 2; // 不推荐
// ✅ 正确:统一使用 Runes
let x = $state(0);
let doubled = $derived(x * 2);
// 1. 使用 $inspect
$inspect(count); // 自动打印变化
// 2. 输出编译结果
import { compile } from 'svelte/compiler';
const { js, css, ast } = compile(source, {
generate: 'client',
dev: true
});
console.log(js.code);
// 3. 查看分析结果
const analysis = analyze_component(parsed, source, options);
console.log(analysis.reactive_statements);
// 4. 使用 Svelte DevTools
// 浏览器扩展查看组件树和状态
| 特性 | Svelte | React |
|---|---|---|
| 运行时 | 编译时 | 运行时 |
| 虚拟 DOM | ❌ 无 | ✅ 有 |
| 包体积 | ~2KB | ~40KB |
| 响应式 | 自动 | useState/useEffect |
| 学习曲线 | 低 | 中 |
| 特性 | Svelte | Vue |
|---|---|---|
| 运行时 | 编译时 | 运行时 + 编译时 |
| 响应式 | Runes ($state) | ref/reactive |
| 模板 | 类 HTML | 类 HTML |
| 作用域样式 | 自动 | scoped 属性 |
| TypeScript | 优秀 | 优秀 |
// SvelteKit 使用 Svelte 编译器
// 提供路由、SSR、SSG 等功能
// +page.svelte
<script>
let { data } = $props();
let count = $state(0);
</script>
<h1>{data.title}</h1>
<button onclick={() => count++}>
{count}
</button>
// +page.server.js
export async function load() {
return { title: 'Hello' };
}
Svelte 6 — modern AST 默认启用
Svelte 7 — 移除 modern 选项
Svelte 编译器 — 编译时优化,零运行时开销
atcfu.com/ai-articles/svelte-compiler/
2026-03-10 | AI 技术洞察