mcpField
S
svelte-core-bestpractices

svelte-core-bestpractices

github.com/sveltejs开发33511.2k 次安装2 天前更新
代码仓库

关于 svelte-core-bestpractices

为使用 Svelte 5 的开发者整理核心实践,涵盖响应式状态、组件结构与样式等主题,并强调优先采用 runes 模式及避免不必要的副作用。

主要功能

  1. 01选择合适的响应式状态
  2. 02用 $derived 计算派生值
  3. 03减少不必要的 $effect
  4. 04采用现代组件与事件语法
  5. 05优化列表渲染与上下文

适用场景

  1. 01编写新的 Svelte 5 组件
  2. 02排查响应式更新问题
  3. 03改进现有 Svelte 代码

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sveltejs/ai-tools --skill svelte-core-bestpractices

安装后在 Claude Code 中输入/svelte-core-bestpractices确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
attach.md
await-expressions.md
bind.md
each.md
hydratable.md
inspect.md
render.md
snippet.md
svelte-reactivity.md

详细介绍

$state

仅对应该具有_响应性_的变量使用 $state 符文——换句话说,只有那些会导致 $effect、$derived 或模板表达式更新的变量才使用它。其他变量都可以是普通变量。

对象和数组($state({...}) 或 $state([...]))具有深层响应性,也就是说,修改它们会触发更新。这样做需要权衡:为了获得细粒度的响应性,对象必须使用代理,因此会带来性能开销。如果处理的是只会被重新赋值(而非修改)的大型对象,请改用 $state.raw。例如,API 响应通常就属于这种情况。

$derived

要根据状态计算值,请使用 $derived,而不是 $effect:

// do this
let square = $derived(num * num);

// don't do this
let square;

$effect(() => {
	square = num * num;
});

说明

$derived 接收的是表达式,_而不是_函数。如果需要使用函数(例如表达式很复杂),请使用 $derived.by。

派生值是可写的——你可以像为 $state 赋值一样为它们赋值,但当其表达式发生变化时,它们会重新求值。

如果派生表达式是对象或数组,它将按原样返回——_不会_具有深层响应性。不过,在少数需要这种响应性的情况下,你可以在 $derived.by 中使用 $state。

$effect

Effect 是一种逃生舱门,通常应避免使用。尤其要避免在 effect 内更新状态。

  • 如果需要将状态同步到 D3 等外部库,通常使用 {@attach ...} 会更简洁
  • 如果需要响应用户交互运行代码,请直接将代码放在事件处理器中,或酌情使用函数绑定
  • 如果需要记录值以进行调试,请使用 $inspect
  • 如果需要观察 Svelte 外部的内容,请使用 createSubscriber

切勿在 effect 的内容外层包上 if (browser) {...} 或类似判断——effect 不会在服务器上运行。

$props

应假定 props 会发生变化。例如,依赖 props 的值通常应使用 $derived:

// @errors: 2451
let { type } = $props();

// do this
let color = $derived(type === 'danger' ? 'red' : 'green');

// don't do this — `color` will not update if `type` changes
let color = type === 'danger' ? 'red' : 'green';

$inspect.trace

$inspect.trace 是用于调试响应性的工具。如果某些内容没有正常更新,或运行次数超出预期,可以将 $inspect.trace(label) 添加为 $effect 或 $derived.by(或它们调用的任意函数)的第一行,以追踪其依赖项并找出触发更新的依赖项。

#key 块

当你希望某个表达式变化时运行过渡/动画,#key 块会很有用,但它的开销可能很大——每次都会卸载其中的所有元素/组件,因此应谨慎使用。例如,与其用 #key 让子组件 <script> 块中的表达式重新运行,不如考虑用 $derived 包装这些表达式。

事件

任何以 on 开头的元素属性都会被视为事件监听器:

<button onclick={() => {...}}>click me</button>


<button {onclick}>...</button>


<button {...props}>...</button>

如果需要为 window 或 document 添加监听器,可以使用 <svelte:window> 和 <svelte:document>:

<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />

避免为此使用 onMount 或 $effect。

代码片段

代码片段是一种定义可复用标记块的方式,可通过 {@render ...} 标签实例化,也可作为 props 传递给组件。它们必须在模板内声明。

{#snippet greeting(name)}
	<p>hello {name}!</p>
{/snippet}

{@render greeting('world')}

说明

在组件顶层声明的代码片段(即不在元素或块内)可在 <script> 中引用。不引用组件状态的代码片段也可在 <script module> 中使用,此时可以将其导出,供其他组件使用。

Each 块

优先使用带键的 each 块——这样可以让 Svelte 精确插入或移除项目,而不是更新现有项目所属的 DOM,从而提升性能。

说明

键必须唯一标识该对象。不要使用索引作为键。

如果需要修改项目(例如使用 bind:value={item.count}),请避免解构。

在 CSS 中使用 JavaScript 变量

如果想在 CSS 中使用 JS 变量,可以使用 style: 指令设置 CSS 自定义属性。

<div style:--columns={columns}>...</div>

然后,你就可以在组件的 <style> 中引用 var(--columns)。

为子组件设置样式

组件 <style> 中的 CSS 仅作用于该组件。如果父组件需要控制子组件的样式,首选方式是使用 CSS 自定义属性:


<Child --color="red" />


<h1>Hello</h1>

<style>
	h1 {
		color: var(--color);
	}
</style>

如果无法做到这一点(例如,子组件来自某个库),可以使用 :global 覆盖样式:

<div>
	<Child />
</div>

<style>
	div :global {
		h1 {
			color: red;
		}
	}
</style>

上下文

考虑使用上下文,而不是在共享模块中声明状态。这样可以将状态限定在应用中需要它的部分,并消除服务器端渲染时状态在用户之间泄漏的可能性。

使用 createContext,而不是 setContext 和 getContext,因为它能提供类型安全。

异步 Svelte

如果使用 5.36 或更高版本,可以使用 await 表达式和 hydratable,直接在组件中使用 promise。请注意,由于这些功能尚未被认为完全稳定,因此需要在 vite.config.js 或 svelte.config.js 的插件选项中启用 experimental.async 选项。

避免使用旧版功能

新代码一律使用 runes 模式,并避免使用已有更现代替代方案的功能:

  • 使用 $state,而不是隐式响应性(例如 let count = 0; count += 1)
  • 使用 $derived 和 $effect,而不是 $: 赋值和语句(但仅在没有更好方案时才使用 effects)
  • 使用 $props,而不是 export let、$$props 和 $$restProps
  • 使用 onclick={...},而不是 on:click={...}
  • 使用 {#snippet ...} 和 {@render ...},而不是 <slot>、$$slots 和 <svelte:fragment>
  • 使用 <DynamicComponent>,而不是 <svelte:component this={DynamicComponent}>
  • 使用 import Self from './ThisComponent.svelte' 和 <Self>,而不是 <svelte:self>
  • 使用带有 $state 字段的类在组件之间共享响应性,而不是使用 stores
  • 使用 {@attach ...},而不是 use:action
  • 在 class 属性中使用 clsx 风格的数组和对象,而不是 class: 指令

评论

支持 Markdown