Svelte Legacy 组件 API 全解:new Component(options)、$set、$on、$destroy 与服务端 render
本文基于 Svelte 官方文档 Legacy 组件 API 参考,系统讲解 Svelte 3/4 的命令行式(imperative)组件 API 及其在 Svelte 5 兼容层中的实现机制。读完本文,你将掌握客户端组件实例化选项(target、anchor、hydrate、intro 等)的完整用法、$set/$on/$destroy 三个生命周期方法的调用方式、accessors 编译选项的影响,以及服务端 Component.render(...) 的返回结构与 svelte/register 的 Node 集成方式,并能结合源码理解 Svelte 5 中 svelte/legacy 兼容层如何把这些旧 API 桥接到新的 signals 运行时。
适用前提:这套 API 面向哪些代码
文档开篇即明确了边界:在 Svelte 3 和 Svelte 4 中,与组件交互的 API 与 Svelte 5 不同;并且该文档并不适用于 Svelte 5 应用中的 legacy 模式组件。
根据 Legacy 概述,Svelte 5 引入了 runes、snippets 和事件属性等显著变化,Svelte 3/4 的部分特性被废弃(目前仍受支持,除非另行说明)。因此这套 legacy 组件 API 文档的目标读者有两类:
- 仍在使用 Svelte 3/4 的开发者;
- 已升级到 Svelte 5、但部分组件尚未完成迁移的开发者——他们可以通过 v5 迁移指南 增量迁移。
从源码结构看,Svelte 5 仓库为这类旧式代码保留了完整的兼容层:legacy-client.js 和 legacy-server.js。两者导出 createClassComponent 和 asClassComponent,其 JSDoc 均标注了 @deprecated,说明它们被定位为"将命令式组件代码迁移到 Svelte 5 的临时方案"。
创建客户端组件:new Component(options)
基本形式
const component = new Component(options);
编译为客户端(即 generate: 'dom',或未指定 generate 选项)的组件是一个 JavaScript 类。文档给出的典型用法如下:
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
// 假设 App.svelte 中有 `export let answer`:
answer: 42
}
});
初始化选项完整参考
文档列出了全部初始化选项,这是本章的核心内容:
| 选项 | 默认值 | 说明 |
|---|---|---|
target |
无(必填) | 渲染目标,必须是一个 HTMLElement 或 ShadowRoot |
anchor |
null |
target 的一个子节点,组件将被渲染到该节点之前 |
props |
{} |
传入组件的属性对象 |
context |
new Map() |
传入组件的根级 context 键值对 Map |
hydrate |
false |
见下文 hydration 说明 |
intro |
false |
为 true 时,首次渲染即播放 transitions,而不是等待后续状态变化才播放 |
一个重要的补充细节:target 中已存在的子节点会保持原位不动(Existing children of target are left where they are)。
hydrate 选项与 DOM 修复行为
hydrate: true 指示 Svelte 升级(upgrade)已有的 DOM(通常来自服务端渲染),而不是新建元素。文档指出了几个关键限制:
- 只有当组件编译时开启了
hydratable: true选项,hydration 才能工作; <head>中的元素只有当服务端渲染代码同样以hydratable: true编译时才能被正确水合——该选项会给<head>中的每个元素加上标记,让组件知道哪些元素是自己在 hydration 时应当移除的;- 通常
target的子节点保持不动,但hydrate: true会移除target的所有子节点。因此anchor不能与hydrate: true同时使用; - 现有 DOM 不需要与组件完全匹配——Svelte 会在一边进行一边"修复(repair)"DOM。
文档给出的 hydration 示例:
/// file: index.js
import App from './App.svelte';
const app = new App({
target: document.querySelector('#server-rendered-html'),
hydrate: true
});
文档同时注明:在 Svelte 5+ 中应改用 mount 替代。
源码印证:Svelte 5 兼容层如何实现 new Component
上述旧 API 在 Svelte 5 中的落地实现在 Svelte4Component 类。可以观察到几个与文档描述直接对应的实现点:
-
props 通过代理实现粗粒度响应。构造函数用一个
Proxy包装{ ...(options.props || {}), $$events: {} },每个 prop 对应一个mutable_source,属性被写入时更新对应的 source。源码注释明确写道:"Replicate coarse-grained props through a proxy … Do not use our state` 不同。 -
mount / hydrate 的分支。实例化时执行
(options.hydrate ? hydrate : mount)(options.component, { target, anchor, props, context, intro: options.intro ?? false, ... }),与文档中hydrate/anchor/intro的默认值和行为一一对应。 -
同步刷新的取舍。构造函数在实例化后调用
flushSync()(除非处于 async 模式、或用户通过sync === false关闭、或 props 中带$$host的自定义元素包装),从而保证new Component(...)返回时 DOM 已就绪——这正是 Svelte 3/4 实例化即完成渲染的旧语义。 -
exports 提升。构造函数遍历实例自身的全部键(跳过
$set/$destroy/$on),用define_property把它们挂到Svelte4Component上,使旧代码中component.someExportedValue的写法继续可用。
$set:程序化更新 props
component.$set(props);
文档说明:component.$set({ x: 1 }) 等价于在组件 <script> 块内执行 x = 1。两条关键性质:
- 这是一个异步更新——调用该方法只是把更新排入下一个微任务,DOM 不会同步更新;
- 示例:
component.$set({ answer: 42 });
文档给出的 Svelte 5 替代方案是用 $state 持有 props 对象:
let props = $state({ answer: 42 });
const component = mount(Component, { props });
// ...
props.answer = 24;
在 legacy-client.js 中,$set 被重写为 Object.assign(props, next)——也就是把新值合并到上面的 props Proxy 上,由 set 陷阱更新对应的 source,再由 signals 调度器安排下一帧的更新。这与文档"排入下一微任务、不同步更新 DOM"的描述一致。
$on:监听组件事件
component.$on(ev, callback);
当组件 dispatch 名为 ev 的事件时,callback 会被调用。该方法返回一个取消监听函数。文档示例:
const off = component.$on('selected', (event) => {
console.log(event.detail.selection);
});
off();
文档注明:在 Svelte 5+ 中应改用回调 prop(callback props)传递事件。
源码层面,[Svelte4Component.on](https://gitcode.com/GitHub_Trending/sv/svelte/blob/b2c22ab66d0978dfa14bb92196a6ab45ad0031a4/packages/svelte/src/legacy/legacy-client.js?utm_source=gitcode_repo_files#L167-L176) 将回调推入按事件名索引的数组(`this.#events[event].push(cb)`),并返回一个通过 `filter` 移除该回调的闭包作为 `off` 函数。由于 `$events` 是在 props `Proxy` 内部注入的(见构造函数中 `{ ...(options.props || {}), $$events: {} }`),组件内部通过自动委托的 legacy `on:` 事件最终会调用到这里注册的监听器;配合 createBubbler 可在事件对象上遍历已注册的回调。
$destroy:销毁组件实例
component.$destroy();
文档说明该方法将组件从 DOM 中移除,并触发所有 onDestroy 处理函数。
在 Svelte 5 兼容层中,实例化时被替换的 #instance.$destroy 直接调用运行时的 unmount(this.#instance)(见 legacy-client.js)。文档同时注明:在 Svelte 5+ 中应改用 unmount 替代。
Component props 与 accessors 编译选项
文档列出了两种直接读写实例属性的写法:
component.prop;
component.prop = value;
规则与限制如下(完整继承自原文档):
- 只有当组件以
accessors: true编译时,每个实例才会拥有与组件每个 prop 对应的 getter/setter; - 通过 setter 赋值会触发同步(synchronous)更新——这与
component.$set(...)默认的异步更新形成对比; - 默认情况下
accessors为false,除非组件被编译为 custom element。
示例:
console.log(component.count);
component.count += 1;
文档注明:在 Svelte 5+ 中这一概念已过时——若想从外部访问属性,直接 export 即可。
这一点与编译器类型定义相互印证:在 CompileOptions 中,accessors?: boolean 的文档写明"若为 true,会为组件的 props 创建 getter 和 setter……使用 customElement: true 编译时此选项默认为 true",默认值为 false,且标注 @deprecated This will have no effect in runes mode——即 runes 模式下 accessors 完全失效,与文档"该概念已过时"的结论一致。
此外,CompileOptions.compatibility 提供了一个 componentApi?: 4 | 5 选项:设为 4 时,编译器会做转换,让 .svelte 文件的默认导出"仍能像 Svelte 4 那样以类的方式在浏览器端实例化(等价于使用 svelte/legacy 的 createClassComponent),或在服务端作为带 .render(...) 方法的对象使用"。这就是在 Svelte 5 项目中让旧式 new Component(options) / Component.render() 代码继续编译运行的官方开关。
服务端组件 API:Component.render(...)
const result = Component.render(...);
文档指出:与客户端组件不同,服务端组件在渲染完成后没有生命周期——它们的唯一职责就是生成 HTML 和 CSS。因此 API 有所不同。
服务端组件暴露一个 render 方法,可传入可选的 props,返回一个包含 head、html、css 三个属性的对象,其中 head 包含渲染过程中遇到的所有 <svelte:head> 元素的内容。
在 Node 中使用 svelte/register
文档说明可以直接在 Node 中通过 svelte/register 引入 Svelte 组件:
require('svelte/register');
const App = require('./App.svelte').default;
const { head, html, css } = App.render({
answer: 42
});
render 方法参数完整参考
.render() 接受两个位置参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
props |
{} |
传入组件的属性对象 |
options |
{} |
选项对象 |
options 对象支持以下选项:
| 选项 | 默认值 | 说明 |
|---|---|---|
context |
new Map() |
传入组件的根级 context 键值对 Map |
带 context 的完整调用示例:
const { head, html, css } = App.render(
// props
{ answer: 42 },
// options
{
context: new Map([['context-key', 'context-value']])
}
);
文档注明:在 Svelte 5+ 中应改用 render 替代。
源码印证:legacy render 如何桥接到新运行时
legacy-server.js 展示了这一桥接的完整过程:
asClassComponent(component)先复用客户端的as_class_component得到类构造器,再挂上一个_render静态方法;_render(props, { context, csp, transformError } = {})内部调用新的服务端运行时render(component, { props, context, csp, transformError })(注意csp参数用于 CSP nonce,源码类型定义 LegacyRenderResult 还包含可选的hashes字段);- 返回结果被"加工(munged)"成旧接口形状:
head读取result.head,html读取result.body(新运行时的字段名是body,旧 API 叫html),css被固定为{ code: '', map: null }——因为现代 Svelte 中样式通常以静态 CSS 提取; - 该对象还实现了
then,使其同时是 PromiseLike——在 async 模式下会等待渲染 Promise 完成后再调用onfulfilled,从而兼容 Svelte 4 中.render()可直接await的行为。
小结:迁移路径速查
将本文各 API 与 Svelte 5 的对应关系汇总如下,便于在存量代码中逐项替换:
| Legacy(Svelte 3/4) | Svelte 5 替代 |
|---|---|
new Component({ target, props, ... }) |
mount(Component, { target, props }),见 20-svelte.md |
component.$set({ x: 1 }) |
用 $state 持有 props 对象并直接修改属性 |
component.$on('selected', cb) |
通过回调 prop 传递事件处理函数 |
component.$destroy() |
unmount(component) |
accessors: true + component.prop = v |
直接 export 需要外部访问的绑定 |
Component.render(props, { context }) |
服务端 render(Component, { props, context }),见 21-svelte-server.md |
在 Svelte 5 项目中若需继续运行旧式命令式代码,编译器侧可通过 compatibility.componentApi: 4 选项(CompileOptions)开启兼容转换,运行时侧则由 svelte/legacy 提供的 createClassComponent/asClassComponent 承担桥接——两者在源码中均被明确标记为仅供迁移期使用的 @deprecated 工具。
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