svelte-core-bestpractices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

$state

$state

Only use the
$state
rune for variables that should be reactive — in other words, variables that cause an
$effect
,
$derived
or template expression to update. Everything else can be a normal variable.
Objects and arrays (
$state({...})
or
$state([...])
) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use
$state.raw
instead. This is often the case with API responses, for example.
仅对需要响应式的变量使用
$state
符文——换句话说,就是那些会触发
$effect
$derived
或模板表达式更新的变量。其他变量都可以用普通变量。
对象和数组(
$state({...})
$state([...])
)会被设置为深度响应式,这意味着对它们的修改会触发更新。但这也存在权衡:为了实现细粒度响应式,这些对象必须被代理,这会带来性能开销。如果你处理的是大型对象且只会重新赋值(而非修改内部属性),可以改用
$state.raw
。例如API响应通常就属于这种情况。

$derived

$derived

To compute something from state, use
$derived
rather than
$effect
:
js
// do this
let square = $derived(num * num);

// don't do this
let square;

$effect(() => {
	square = num * num;
});
[!NOTE]
$derived
is given an expression, not a function. If you need to use a function (because the expression is complex, for example) use
$derived.by
.
Deriveds are writable — you can assign to them, just like
$state
, except that they will re-evaluate when their expression changes.
If the derived expression is an object or array, it will be returned as-is — it is not made deeply reactive. You can, however, use
$state
inside
$derived.by
in the rare cases that you need this.
如果需要根据现有状态计算值,请使用
$derived
而非
$effect
js
// 推荐写法
let square = $derived(num * num);

// 不推荐写法
let square;

$effect(() => {
	square = num * num;
});
[!NOTE]
$derived
接收的是一个表达式,而非函数。如果需要使用函数(比如表达式逻辑复杂时),请使用
$derived.by
派生值是可写的——你可以像给
$state
赋值一样给它赋值,不过当它的依赖表达式变化时,派生值会重新计算。
如果派生表达式的结果是对象或数组,它会直接返回,不会被设置为深度响应式。不过在极少数需要的情况下,你可以在
$derived.by
内部使用
$state

$effect

$effect

Effects are an escape hatch and should mostly be avoided. In particular, avoid updating state inside effects.
  • If you need to sync state to an external library such as D3, it is often neater to use
    {@attach ...}
  • If you need to run some code in response to user interaction, put the code directly in an event handler or use a function binding as appropriate
  • If you need to log values for debugging purposes, use
    $inspect
  • If you need to observe something external to Svelte, use
    createSubscriber
Never wrap the contents of an effect in
if (browser) {...}
or similar — effects do not run on the server.
$effect
是一个“逃生舱”,应尽量避免使用。尤其要避免在
$effect
内部更新状态。
  • 如果你需要将状态与D3等外部库同步,通常使用
    {@attach ...}
    会更简洁
  • 如果你需要响应用户交互执行代码,直接将代码放在事件处理函数中,或根据情况使用函数绑定
  • 如果你需要记录值用于调试,请使用
    $inspect
  • 如果你需要监听Svelte外部的内容,请使用
    createSubscriber
永远不要在
$effect
内部用
if (browser) {...}
之类的代码包裹内容——
$effect
不会在服务器端运行。

$props

$props

Treat props as though they will change. For example, values that depend on props should usually use
$derived
:
js
// @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';
要将props视为可能会变化的值。例如,依赖props的值通常应使用
$derived
js
// @errors: 2451
let { type } = $props();

// 推荐写法
let color = $derived(type === 'danger' ? 'red' : 'green');

// 不推荐写法——如果type变化,color不会更新
let color = type === 'danger' ? 'red' : 'green';

$inspect.trace

$inspect.trace

$inspect.trace
is a debugging tool for reactivity. If something is not updating properly or running more than it should you can add
$inspect.trace(label)
as the first line of an
$effect
or
$derived.by
(or any function they call) to trace their dependencies and discover which one triggered an update.
$inspect.trace
是用于调试响应式的工具。如果某个内容没有正确更新,或者运行次数超出预期,你可以在
$effect
$derived.by
(或它们调用的任何函数)的第一行添加
$inspect.trace(label)
,以追踪它们的依赖项,找出触发更新的原因。

Events

事件

Any element attribute starting with
on
is treated as an event listener:
svelte
<button onclick={() => {...}}>click me</button>

<!-- attribute shorthand also works -->
<button {onclick}>...</button>

<!-- so do spread attributes -->
<button {...props}>...</button>
If you need to attach listeners to
window
or
document
you can use
<svelte:window>
and
<svelte:document>
:
svelte
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
Avoid using
onMount
or
$effect
for this.
任何以
on
开头的元素属性都会被视为事件监听器:
svelte
<button onclick={() => {...}}>click me</button>

<!-- 属性简写也适用 -->
<button {onclick}>...</button>

<!-- 展开属性同样适用 -->
<button {...props}>...</button>
如果你需要给
window
document
添加监听器,可以使用
<svelte:window>
<svelte:document>
svelte
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
避免使用
onMount
$effect
来实现此功能。

Snippets

代码片段(Snippets)

Snippets are a way to define reusable chunks of markup that can be instantiated with the
{@render ...}
tag, or passed to components as props. They must be declared within the template.
svelte
{#snippet greeting(name)}
	<p>hello {name}!</p>
{/snippet}

{@render greeting('world')}
[!NOTE] Snippets declared at the top level of a component (i.e. not inside elements or blocks) can be referenced inside
<script>
. A snippet that doesn't reference component state is also available in a
<script module>
, in which case it can be exported for use by other components.
代码片段是定义可复用标记块的方式,你可以用
{@render ...}
标签实例化它们,或者作为props传递给组件。它们必须在模板内部声明。
svelte
{#snippet greeting(name)}
	<p>hello {name}!</p>
{/snippet}

{@render greeting('world')}
[!NOTE] 在组件顶层(即不在元素或块内部)声明的代码片段可以在
<script>
内部引用。不引用组件状态的代码片段也可以在
<script module>
中使用,这种情况下它可以被导出供其他组件使用。

Each blocks

Each块

Prefer to use keyed each blocks — this improves performance by allowing Svelte to surgically insert or remove items rather than updating the DOM belonging to existing items.
[!NOTE] The key must uniquely identify the object. Do not use the index as a key.
Avoid destructuring if you need to mutate the item (with something like
bind:value={item.count}
, for example).
优先使用带key的each块——这样可以提升性能,因为Svelte可以精准地插入或移除项目,而非更新现有项目对应的DOM。
[!NOTE] key必须能唯一标识对象。不要使用索引作为key。
如果需要修改项目(比如使用
bind:value={item.count}
),请避免解构。

Using JavaScript variables in CSS

在CSS中使用JavaScript变量

If you have a JS variable that you want to use inside CSS you can set a CSS custom property with the
style:
directive.
svelte
<div style:--columns={columns}>...</div>
You can then reference
var(--columns)
inside the component's
<style>
.
如果你想在CSS中使用JS变量,可以通过
style:
指令设置CSS自定义属性。
svelte
<div style:--columns={columns}>...</div>
然后你就可以在组件的
<style>
中引用
var(--columns)
了。

Styling child components

子组件样式

The CSS in a component's
<style>
is scoped to that component. If a parent component needs to control the child's styles, the preferred way is to use CSS custom properties:
svelte
<!-- Parent.svelte -->
<Child --color="red" />

<!-- Child.svelte -->
<h1>Hello</h1>

<style>
	h1 {
		color: var(--color);
	}
</style>
If this impossible (for example, the child component comes from a library) you can use
:global
to override styles:
svelte
<div>
	<Child />
</div>

<style>
	div :global {
		h1 {
			color: red;
		}
	}
</style>
组件
<style>
中的CSS是作用域隔离的。如果父组件需要控制子组件的样式,推荐的方式是使用CSS自定义属性:
svelte
<!-- Parent.svelte -->
<Child --color="red" />

<!-- Child.svelte -->
<h1>Hello</h1>

<style>
	h1 {
		color: var(--color);
	}
</style>
如果这种方式不可行(比如子组件来自第三方库),你可以使用
:global
来覆盖样式:
svelte
<div>
	<Child />
</div>

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

Context

上下文(Context)

Consider using context instead of declaring state in a shared module. This will scope the state to the part of the app that needs it, and eliminate the possibility of it leaking between users when server-side rendering.
Use
createContext
rather than
setContext
and
getContext
, as it provides type safety.
考虑使用上下文替代在共享模块中声明状态。这样可以将状态的作用域限定在应用需要它的部分,并且在服务端渲染时可以避免状态在用户之间泄露。
使用
createContext
而非
setContext
getContext
,因为它能提供类型安全。

Async Svelte

异步Svelte

If using version 5.36 or higher, you can use await expressions and hydratable to use promises directly inside components. Note that these require the
experimental.async
option to be enabled in
svelte.config.js
as they are not yet considered fully stable.
如果你使用的是5.36或更高版本,可以使用await表达式hydratable在组件中直接使用Promise。注意这些功能需要在
svelte.config.js
中启用
experimental.async
选项,因为它们尚未完全稳定。

Avoid legacy features

避免使用旧特性

Always use runes mode for new code, and avoid features that have more modern replacements:
  • use
    $state
    instead of implicit reactivity (e.g.
    let count = 0; count += 1
    )
  • use
    $derived
    and
    $effect
    instead of
    $:
    assignments and statements (but only use effects when there is no better solution)
  • use
    $props
    instead of
    export let
    ,
    $$props
    and
    $$restProps
  • use
    onclick={...}
    instead of
    on:click={...}
  • use
    {#snippet ...}
    and
    {@render ...}
    instead of
    <slot>
    and
    $$slots
    and
    <svelte:fragment>
  • use
    <DynamicComponent>
    instead of
    <svelte:component this={DynamicComponent}>
  • use
    import Self from './ThisComponent.svelte'
    and
    <Self>
    instead of
    <svelte:self>
  • use classes with
    $state
    fields to share reactivity between components, instead of using stores
  • use
    {@attach ...}
    instead of
    use:action
  • use clsx-style arrays and objects in
    class
    attributes, instead of the
    class:
    directive
新代码请始终使用符文模式,避免使用有现代替代方案的旧特性:
  • 使用
    $state
    替代隐式响应式(例如
    let count = 0; count += 1
  • 使用
    $derived
    $effect
    替代
    $:
    赋值和语句(但只有在没有更好方案时才使用
    $effect
  • 使用
    $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:
    指令