Skip to main content

svelte-runes

Svelte 5 Runes 响应式系统技能。当用户需要使用 $state/$derived/$effect/$props/$bindable/$inspect/$host 等符文,或理解 Svelte 5 显式响应式与 Svelte 4 隐式响应式的区别时使用。

Zur Installation springen

Quellinformationen

Repository
full-stack-skills/svelte-skills
Letzte Quellaktivität
11. September 2026 um 13:43
Erkannte Sprache von SKILL.md
Chinesisch
Sterne
3
Forks
2

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
18 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
svelte-runes
license
Apache-2.0
description
Svelte 5 Runes 响应式系统技能。当用户需要使用 $state/$derived/$effect/$props/$bindable/$inspect/$host 等符文,或理解 Svelte 5 显式响应式与 Svelte 4 隐式响应式的区别时使用。
# Svelte Runes Reference (Svelte 5) 本技能覆盖 Svelte 5 的 Runes(符文)系统。Runes 是 Svelte 5 引入的显式响应式语法,取代了 Svelte 4 的隐式 `let` 声明和 `$:` 语句。 ## Capability Boundaries ### ✅ 强项 1. Svelte 5 显式响应式系统(Runes)的完整参考:$state / $derived / $effect / $props / $bindable / $inspect / $host 2. 详细的子段(Proxy 行为、依赖追踪、cleanup、pre/tracking/pending/root 等) 3. 跨模块共享状态模式 + `.svelte.js` 文件约定 4. Svelte 4 → Svelte 5 迁移提示(隐式 vs 显式响应式) 5. 完整的 Quick Fixes / Gotchas / FAQ 速查 ### ⚠️ 限制 1. 本技能专注于 Svelte 5 Runes 体系,不覆盖 Svelte 4 隐式响应式(`let` + `$:`) 2. 不包含动画 / 过渡 / 模板语法的深入内容(改用 `svelte-template-syntax`) 3. 不包含组件生命周期 / stores / context 的高级用法(仅在 Svelte Runes 相关处提及) ### ❌ Out of Scope(不该用本技能的场景) 1. **Svelte 4 隐式响应式** → 不适用,改用 Svelte 4 文档 2. **模板块语法({#if}/{#each}/{#await}/{#snippet})** → 改用 `svelte-template-syntax` 3. **transition/animate** → 改用 `svelte-template-syntax` 4. **Stores(writable/derived)** → 改用 Svelte 5 文档对应章节(与 Runes 互补) ## Data Privacy 本技能不收集、存储或传输任何用户数据。所有代码示例仅用于本地开发参考。 ## Workflow Step 1. **识别用户需要的 Rune** — `$state` / `$derived` / `$effect` / `$props` / `$bindable` / `$inspect` / `$host` Step 2. **查对应章节** — SKILL.md 给概览,深度细节见 `references/$X-deep.md` Step 3. **必要时看示例** — `examples/$X-patterns.md` 提供可复制代码 Step 4. **查 Quick Fixes / Gotchas / FAQ** — 三个速查表覆盖常见错误 Step 5. **Svelte 4 迁移场景** — 关注"Runes Overview"对比段 ## When to use this skill 当用户需要理解或使用 `$state`、`$derived`、`$effect`、`$props`、`$bindable`、`$inspect`、`$host` 等符文,或需要将 Svelte 4 代码迁移到 Svelte 5 时使用本技能。 ## Critical: Runes Overview Runes 是以 `$` 为前缀的符号,类似于函数调用语法: ```js let message = $state('hello'); ``` 关键区别: - **无需导入** — Runes 是语言内置关键字 - **不是值** — 不能赋值给变量或作为函数参数 - **位置敏感** — 仅在特定位置有效(编译器会报错) | Rune | 用途 | |------|------| | `$state` | 创建响应式状态 | | `$derived` | 声明派生计算值 | | `$effect` | 声明副作用 | | `$props` | 声明组件属性 | | `$bindable` | 可绑定 prop | | `$inspect` | 开发调试 | | `$host` | 自定义元素访问 | ## Critical: $state 创建响应式状态,UI 在状态变化时自动更新。 ```svelte let count = $state(0); let user = $state({ name: 'Ada', age: 30 }); ``` ### 深层响应式代理 `$state` 对数组和简单对象自动创建深层 Proxy,属性变更自动触发更新: ```js let todos = $state([ { done: false, text: 'task' } ]); todos[0].done = true; // ✅ 触发更新 todos.push({ done: false }); // ✅ 触发更新 ``` ### $state.raw(避免深层代理) 适用于大数组和无需深层变更的场景: ```js let list = $state.raw([]); // 只能重新赋值,不能 .push() list = [...list, newItem]; // ✅ list.push(newItem); // ❌ 无效 ``` > `$state.raw` 内部仍可包含响应式状态(如原始数组里放代理对象)。 ### $state.snapshot(静态快照) 获取深层代理的只读快照(用于传给外部 API): ```svelte console.log($state.snapshot(proxyObject)); ``` > 若值有 `toJSON()` 方法,snapshot 会克隆 `toJSON()` 的返回值。 ### $state.eager(即时 UI 更新) 用于 `await` 表达式中立即更新 UI: ```svelte <nav> <a href="/" aria-current={$state.eager(pathname) === '/' ? 'page' : null}>home</a> </nav> ``` ### 类字段中的 $state ```js class Counter { count = $state(0); // 公共字段 #value = $state(0); // 私有字段 constructor(start = 0) { this.value = $state(start); // constructor 中初始化 } // 箭头函数字段 → this 自动绑定 reset = () => { this.count = 0; }; } ``` > 编译器将 `$state` 字段转换为原型上的 `get`/`set` 方法,指向私有字段。**这些属性不可枚举**。注意 `this` 绑定:方法直接传给事件处理器会丢失 `this`,用箭头函数字段或内联 `() => todo.reset()`。 ### 内置响应式类(svelte/reactivity) 普通 `Map`/`Set`/`Date`/`URL` 不响应 —— 改用响应式版本: ```js import { SvelteSet, SvelteMap, SvelteDate, SvelteURL } from 'svelte/reactivity'; const tags = new SvelteSet(['a', 'b']); const cache = new SvelteMap(); tags.add('c'); // ✅ 触发更新 cache.set('a', 1); // ✅ 触发更新 ``` ### 解构陷阱 解构后丢失响应式(与普通 JS 行为一致): ```js let { name, age } = $state({ name: 'Ada', age: 30 }); name = 'Bob'; // ❌ 不会触发更新,原对象不变 ``` ### 跨模块传递状态 `.svelte.js/.svelte.ts` 文件中可使用 Runes,但不能直接 `export let` 重新赋值的 `$state`: ```js // ❌ 不可行:另一文件读到的会是 Signal 对象 export let count = $state(0); // ✅ 方案1:不重新赋值整个对象 export const counter = $state({ count: 0 }); export function increment() { counter.count += 1; } // ✅ 方案2:模块内私有 + 函数导出 let _count = $state(0); export function getCount() { return _count; } export function setCount(n) { _count = n; } ``` ### 将 state 传入函数 —— getter 模式 JavaScript 是**按值传递**,`$state` 同理。若函数需读取最新值,传入 getter: ```js /** @param {() => number} getA @param {() => number} getB */ function add(getA, getB) { return () => getA() + getB(); } let a = $state(1); let b = $state(2); const total = add(() => a, () => b); console.log(total()); // 3 a = 3; b = 4; console.log(total()); // 7 ``` > 也可借助 proxy 属性或 get/set 属性实现"实时读取"。文档:"Note that 'functions' is broad — it encompasses properties of proxies and get/set properties." ## Critical: $derived 声明派生值——基于已有状态的只读计算值。 ```svelte let count = $state(0); let doubled = $derived(count * 2); ``` > 表达式**必须纯净** —— 内部不能修改 `$state`(编译器报错)。 ### $derived.by(复杂派生) ```svelte let total = $derived.by(() => { let sum = 0; for (const item of items) sum += item.price; return sum; }); ``` ### 派生值覆盖(Svelte 5.25+,乐观 UI) ```svelte let likes = $derived(post.likes); async function onclick() { likes += 1; // 即时乐观更新 try { await like(); } catch { likes -= 1; // 回滚 } } ``` > `const` 派生只读;用 `let` 才能重新赋值。文档:"Prior to Svelte 5.25, deriveds were read-only." ### 理解依赖追踪 `$derived` 内部同步读取的所有 `$state`/`$derived` 都是依赖: ```js let a = Promise.resolve(1); let b = 2; let sum = $derived(await a + b); // a 和 b 都是依赖(await 之后的同步读取也会追踪) ``` > 仅**表达式自身**的 `await` 后的同步读才算依赖;**调用函数**内部的 `await` 不计入。 用 `untrack` 排除非依赖值。 ### Push-pull 响应 派生值**只在被读取时**重新计算(pull),但状态变化时**立即通知**所有依赖(push)。若派生返回的引用未变,下游不更新: ```svelte let count = $state(0); let large = $derived(count > 10); // 布尔 // 大文本节点只在 large 变化时重渲染,而非 count ``` > 派生内避免 `() => ({})`、`[...].map(...)` 这类返回新引用的写法 —— 会让下游始终重算。 ### 派生不解构代理 `$derived` 不会把返回值包成 Proxy —— 修改派生返回对象的属性会影响到底层 `$state`: ```js let items = $state([...]); let selected = $derived(items[0]); selected.name = 'new'; // ✅ 影响 items[0].name ``` ### 派生解构 ```js let { first, last } = $derived(user); // 等价于: // let first = $derived(user.first); // let last = $derived(user.last); ``` ## Critical: $effect 声明副作用——DOM 操作、第三方库调用、网络请求等。**不应**用 `$effect` 同步状态。 ```svelte $effect(() => { document.title = `count: ${count}`; return () => { /* 清理函数 */ }; }); ``` ### 生命周期 - 挂载后**首次执行** - 状态变化时 **microtask 调度**(批量) - DOM 更新**之后**执行 - 仅浏览器执行(SSR 自动跳过) - 可在组件任意位置调用,只要在父 effect 运行期间 ### 依赖追踪 自动追踪 `$state`/`$derived` 的同步读取: ```svelte $effect(() => { // color 和 size 是依赖 ctx.fillStyle = color; ctx.fillRect(0, 0, size, size); }); ``` ### 异步不追踪 `await` 之后和 `setTimeout` 内部的读取**不**追踪: ```js $effect(() => { ctx.fillStyle = color; // ✅ 追踪 setTimeout(() => { ctx.fillRect(0, 0, size, size); // ❌ size 不追踪 }, 0); }); ``` ### 条件分支决定依赖 effect **只追踪上次运行时实际读取的状态**: ```ts $effect(() => { if (condition) { confetti({ colors: [color] }); } else { confetti(); } }); // condition=false 时 color 不再是依赖 ``` ### $effect.pre(DOM 更新前运行) ```svelte $effect.pre(() => { if (!div) return; // 挂载前跳过 messages.length; // 显式依赖追踪 // DOM 更新前执行(如滚动位置计算) }); ``` ### $effect.tracking(判断追踪上下文) ```svelte console.log($effect.tracking()); // false(组件初始化) $effect(() => { console.log($effect.tracking()); // true(effect 内) }); ``` > 用于 `createSubscriber` 这类工具:仅在状态被追踪时订阅,事件处理器中不订阅。 ### $effect.pending()(待定 Promise 数量) 返回**当前 boundary**(不含子 boundary)中待定的 Promise 数量: ```svelte <script> let a = $state(1); let b = $state(2); async function add(a, b) { await new Promise(f => setTimeout(f, 500)); return a + b; } </script> <button onclick={() => a++}>a++</button> <button onclick={() => b++}>b++</button> <p>{a} + {b} = {await add(a, b)}</p> {#if $effect.pending()} <span>pending: {$effect.pending()}</span> {/if} ``` ### $effect.root()(独立作用域) 创建**非追踪**作用域,不随组件销毁自动清理: ```js import { flushSync } from 'svelte'; const destroy = $effect.root(() => { let count = $state(0); $effect(() => console.log(count)); return () => { /* cleanup */ }; }); count = 1; flushSync(); // 立即跑 pending effects // later... destroy(); ```
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen