svelte 核心包深度解析:从"编译器而非框架"的设计理念到客户端/服务端运行时
Svelte 核心包 packages/svelte 是整个项目的灵魂:它把 .svelte 声明式组件在编译期转换为高效 JavaScript,而非在浏览器里维护虚拟 DOM。本文以该包的 README 为骨架,结合 package.json、编译器入口 与 客户端运行时 的源码实现展开,读完你将掌握:用 SvelteKit 命令从零启动一个应用的完整流程、svelte/compiler 提供的 compile/parse/migrate API 与全部可配置编译选项、以及通过 mount/hydrate 和条件导出区分客户端/服务端运行时的机制。
一、Svelte 是什么:一个"手术式"更新 DOM 的编译器
packages/svelte/README.md 对 Svelte 的定位只有一句话:
Svelte is a new way to build web applications. It's a compiler that takes your declarative components and converts them into efficient JavaScript that surgically updates the DOM.
关键在两个词:compiler 和 surgically(外科手术式)。Svelte 不依赖运行时的虚拟 DOM 与 diff 算法,而是在编译期就精确计算出"状态变化时该动 DOM 的哪一块"。官方文档 Overview 用一个组件说明了输入输出关系——一个包含 <script>、<button onclick={greet}> 和 <style> 的 App.svelte,会被转换成"lean, tightly optimized JavaScript":
<script>
function greet() {
alert('Welcome to Svelte!');
}
</script>
<button onclick={greet}>click me</button>
<style>
button {
font-size: 2em;
}
</style>
"手术式更新"在源码中有直接证据:编译产物并不内联所有逻辑,而是从内部的 $ 命名空间导入大量细粒度 DOM 操作原语。packages/svelte/src/internal/client/index.js 导出了 append、text、set_attribute、bind_checked、set_style 等数十个原子操作,以及 if/each/await/key/snippet 等块级构建器——编译生成的代码就是通过这些函数逐节点精确操作 DOM,而非整树重建。
编译主流程:parse → analyze → transform
packages/svelte/src/compiler/index.js#L23-L60 中的 compile(source, options) 是整个包的 API 核心,其执行链路为:
- 去 BOM 并重置状态:
remove_bom(source)防止字节序标记破坏模板生成逻辑,state.reset(...)根据warningFilter和filename初始化诊断上下文; - 选项校验:
validate_component_options(options, '')完成(详见第四节); - Phase 1 解析:
_parse(source)产出内部 AST;若检测到 TypeScript(parsed.metadata.ts),先经remove_typescript_nodes剥离类型标注; - Phase 2 分析:
analyze_component(parsed, source, combined_options)进行作用域、响应式依赖等语义分析; - Phase 3 转换:
transform_component(analysis, source, combined_options)生成最终 JavaScript 代码; - 返回结果:
result.ast通过to_public_ast按modernAst选项返回 modern 或 legacy 两种公共 AST 形态。
同一文件还导出四个面向不同使用场景的函数:
| 函数 | 用途 | 源码位置 |
|---|---|---|
compile(source, options) |
将 .svelte 组件源码编译为导出组件的 JS 模块 |
index.js#L23-L60 |
compileModule(source, options) |
编译含 runes 的纯 JavaScript 模块(即 .svelte.js 文件) |
index.js#L69-L76 |
parse(source, { modern, loose }) |
仅解析并返回 AST,不编译 | index.js#L117-L123 |
parseCss(source) |
解析 <style> 内容,返回 StyleSheetFile AST |
index.js#L131-L147 |
其中 parse 的两个选项值得注意:modern(Svelte 5 中默认 false,返回 legacy AST;源码注释表明 Svelte 6 将默认返回 modern AST 并在 Svelte 7 移除该选项)与 loose(5.13.0 起提供,尽力在输入无法成功编译时也返回 AST,适合编辑器语言服务)。文件末尾还有一个显式的废弃导出——调用 walk() 会直接抛出错误,提示改用 estree-walker(index.js#L195-L199),另外编译器还导出 migrate(用于 v4→v5 语法迁移)、preprocess(预处理器工具)与 VERSION。
二、安装与运行:README 的 Getting Started 完整路径
README 给出的起步方式(原文继承):
npx sv create my-app
cd my-app
npm install
npm run dev
README 明确推荐用 SvelteKit 来构建完整应用——它是 Svelte 团队官方配套的应用框架。官方 Getting Started 文档 补充了两条平行的技术路线:
- SvelteKit 路线(推荐):
npx sv create myapp一步完成脚手架,官方说法是"即使你还不懂 Svelte 也能跑起来,SvelteKit 附加的高级特性可以之后再学"; - Vite 直连路线:通过
npm create vite@latest选择svelte模板(或给现有项目安装vite-plugin-svelte插件),npm run build会在dist目录产出 HTML、JS、CSS 文件。这条路线通常还需要自行挑选一个路由库。
配套工程工具方面,文档建议安装官方维护的 VS Code 扩展获取编辑器支持,并可用 npx sv check 在命令行做类型/代码检查。
运行环境有两个硬性前提,来自 packages/svelte/package.json#L8-L10:
"engines": { "node": ">=18" },即 Node 18 及以上;"type": "module",包本身以 ESM 形态发布。
至于配置文件长什么样,仓库自身就是一份示例:svelte.config.js 仅 8 行,演示了通过 compilerOptions.experimental.async 开启编译器实验特性:
export default {
compilerOptions: {
experimental: {
async: true
}
}
};
三、包导出结构:一个 npm 包、十四个子路径
package.json 中 "name": "svelte","description": "Cybernetically enhanced web apps",当前仓库版本为 5.57.0,MIT 许可。exports 字段(package.json#L22-L112)将运行时能力拆分为多个子路径,按需导入可显著缩小打包体积:
| 子路径 | 提供能力 |
|---|---|
svelte |
根入口:mount/hydrate/unmount、生命周期与上下文 API(条件导出,见下) |
svelte/compiler |
compile、compileModule、parse、parseCss、preprocess、migrate(require 条件指向预编译的 compiler/ 目录) |
svelte/action、svelte/attachments |
Action 与 attachment 的类型声明 |
svelte/animate、svelte/easing、svelte/transition、svelte/motion |
元素间动画、缓动函数、进出场过渡、spring/tweened 动效 |
svelte/events、svelte/reactivity、svelte/reactivity/window |
事件工具、响应式原语(SvelteMap/SvelteSet/SvelteDate 等)及其 window 扩展 |
svelte/server |
服务端辅助(渲染输出类型 RenderOutput、SyncRenderOutput 等,见 CHANGELOG 5.57.0) |
svelte/store、svelte/legacy |
传统 stores API 与 Svelte 4 风格兼容层(均为 browser/worker 条件导出) |
svelte/elements |
仅类型声明(elements.d.ts) |
svelte/internal* |
编译器生成代码专用的内部实现(internal/client、internal/server、internal/flags/*),不属于公共 API |
三个条件导出细节值得展开:
- 根入口的 browser/worker 分流:
"."在browser条件下解析到 src/index-client.js,在worker/default下解析到 src/index-server.js;./legacy、./reactivity、./store采用同一模式。这意味着同一份import { mount } from 'svelte'代码,打包器会根据目标环境自动选择客户端或服务端实现,无需用户改动 import。 svelte/compiler的 require 条件:require条件指向 packages/svelte/compiler("type": "commonjs"),default指向源码入口。即编译器同时支持 CJS(典型场景:Node 环境下的require('svelte/compiler'))与 ESM 消费。- 构建脚本:
"build"为rollup -c && pnpm generate && node scripts/check-treeshakeability.js(package.json#L141)——除了 rollup 打包,还包含一个专门的 treeshakeability 检查,验证发布产物可被打包器充分摇树,呼应"零运行时框架代码、按需引入"的设计主张。
运行时依赖(package.json#L173-L189)也侧面印证编译器架构:acorn + @sveltejs/acorn-typescript 负责 JavaScript/TypeScript 语法解析,zimmerframe 用于 AST 遍历(to_public_ast 中可见其 walk 调用),magic-string 做保留源码映射的字符串级改写,esrap 负责代码生成,devalue 用于 SSR 状态序列化,clsx 支撑 class: 指令。
四、compile 的编译选项:可配置项、默认值与已移除项
所有选项的取值范围与默认值由 packages/svelte/src/compiler/validate-options.js 集中定义,未识别的选项会直接抛出 options_unrecognised 错误(而非静默忽略),这点对排查拼写错误很重要。
公共选项(common_options)
源码见 validate-options.js#L11-L49:
| 选项 | 默认值 | 说明 |
|---|---|---|
filename |
'(unknown)' |
组件文件名,用于错误信息与 cssHash 生成 |
rootDir |
process.cwd()(Deno 下为 Deno.cwd()) |
与 Svelte 4 行为保持一致,便于 Deno 场景 |
dev |
false |
开发模式:插入更多校验与调试信息 |
generate |
'client' |
取值 'client' | 'server' | false;传旧值 'dom'/'ssr' 会自动映射并给出一次性警告 |
warningFilter |
() => true |
返回 false 可抑制特定警告 |
experimental |
{ async: false } |
实验特性开关,仓库自身的 svelte.config.js 即开启其中 async 一项 |
组件选项(component_options)
源码见 validate-options.js#L51-L165:
| 选项 | 默认值 | 说明 |
|---|---|---|
css |
'external' |
'external'(外部样式文件,默认推荐)或 'injected';布尔值与 'none' 均已移除,传入会直接报错 |
cssHash |
函数 | 默认生成 svelte-${hash(filename ?? css)} 形式的 scoping 类名 |
cssOutputFilename |
undefined |
外部 CSS 产物的目标文件名 |
namespace |
'html' |
取值 'html' | 'mathml' | 'svg' |
compatibility |
{ componentApi: 5 } |
componentApi 可取 [4, 5],保留旧版组件实例 API 的兼容开关 |
customElement |
false |
是否按自定义元素编译 |
modernAst |
false |
compile 返回的 AST 形态(modern 或 legacy) |
hmr |
false |
生成代码是否包含 HMR 处理逻辑 |
preserveWhitespace / preserveComments |
false |
是否保留模板空白/注释 |
runes |
undefined |
支持函数形式的参数化选项,可由组件自身声明 |
sourcemap / outputFilename |
undefined |
Source map 输入与产物文件名 |
已移除与废弃的选项(迁移时的常见坑)
同一文件用 removed/deprecate 工具函数管理了完整的选项生命周期:
- 传入即报错(
removed):format(Svelte 4 起只输出 ESM)、tag(改用组件内<svelte:options customElement="tag-name" />)、sveltePath、errorMode、varsReport(均建议用 TypeScript 的verbatimModuleSyntax替代); - 传入即警告(
warn_removed/deprecate):loopGuardTimeout、enableSourcemap、hydratable、accessors、immutable; legacy选项已彻底移除,提示信息指明应改用compatibility.componentApi。
这些迁移提示对从 Svelte 4 升级的团队尤其实用——每个被移除选项的报错信息都自带了下一步操作指引。
五、运行时入口:mount/hydrate 与客户端/服务端的差异
mount/hydrate/unmount 由 src/internal/client/render.js 实现并通过根入口导出:mount(component, options) 将组件挂载到 target 并返回组件 exports(若编译时启用 accessors: true 还可返回 props);源码注释明确说明"除非 intro 选项设为 false,初次渲染会播放进出场过渡";hydrate 则面向服务端渲染产出的已有 DOM 进行激活,支持 props、events、context 等选项。
两个入口文件之间的差异是理解 Svelte SSR 模型的关键:
- 客户端 src/index-client.js:导出完整生命周期与工具 API——
onMount/onDestroy、tick/untrack/settled、flushSync/fork、getAbortSignal(在$effect/$derived内获取随反应销毁而中止的AbortSignal,官方示例即用于取消过期的fetch)、上下文 API(createContext/getContext/setContext等)、createRawSnippet。同时,beforeUpdate/afterUpdate/createEventDispatcher已标注@deprecated,源码注释分别指向$effect.pre、$effect与"callback props 或$host()rune"作为替代方案(index-client.js#L189-L233)。 - 服务端 src/index-server.js:
onMount/beforeUpdate/afterUpdate全部是 no-op(服务端无挂载概念),mount/hydrate/unmount/fork直接抛出lifecycle_function_unavailable错误,tick/settled是立即 resolve 的空异步函数,createEventDispatcher返回 no-op;而onDestroy是真实可用的——它会把回调注册到服务端渲染器的销毁钩子上(这正是 CHANGELOG 5.57.0 中"server render 抛错时也要执行onDestroy回调"修复项所保护的语义)。 - 开发期防错:客户端入口在
DEV模式下会为$state、$effect、$derived、$inspect、$props、$bindable六个 rune 安装全局 getter——在编译产物之外(如普通 JS 模块)误用 rune 时,会抛出指向明确错误信息的异常而非静默失败(index-client.js#L12-L44)。
六、Changelog 与版本追踪
README 指明该包的变更历史在仓库内的 CHANGELOG.md(共 6000 余行)。当前版本 5.57.0 的条目展示了典型的发布节奏:Minor 项如从 svelte/server 导出 RenderOutput/SyncRenderOutput/Csp 等类型、createContext 增加 has 函数、<select> 支持 defaultValue;Patch 项既有 bug 修复(如 hydration 时保留 spread 属性的 defaultChecked)也有性能优化(如"legacy $: 响应式语句排序的 O(n²)→O(n) Map 查找")。排查行为差异时,按版本号回查该文件是最直接的手段。
七、许可与社区
packages/svelte/README.md 声明 Svelte 是一个 MIT 许可的开源项目(协议全文见仓库根目录 LICENSE.md),持续开发完全依靠志愿者完成;README 同时建议通过 Open Collective 成为 backer 来分担开发相关支出(资金用于补偿开发开支)。社区交流渠道与学习入口方面,README 指向官方站点的教程(tutorial)、示例(examples)、REPL 以及 Discord 聊天室,适合作为遇到具体问题时的求助路径。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00