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.
Effects是一个“逃生舱”,应尽量避免使用。尤其要避免在Effects中更新状态。
  • 如果你需要将状态与D3等外部库同步,通常使用
    {@attach ...}
    会更简洁
  • 如果你需要响应用户交互执行代码,请直接将代码放在事件处理程序中,或根据情况使用函数绑定
  • 如果你需要记录值用于调试,请使用
    $inspect
  • 如果你需要观察Svelte外部的内容,请使用
    createSubscriber
切勿将Effect的内容包裹在
if (browser) {...}
或类似语句中——Effects不会在服务器端运行。

$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={() => {...}}>点击我</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.
Snippets是一种定义可复用标记块的方式,你可以使用
{@render ...}
标签实例化它们,或者将它们作为props传递给组件。它们必须在模板内部声明。
svelte
{#snippet greeting(name)}
	<p>你好 {name}!</p>
{/snippet}

{@render greeting('世界')}
[!NOTE] 在组件顶层(即不在元素或块内部)声明的Snippets可以在
<script>
中引用。如果某个Snippet不依赖组件状态,它也可以在
<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="红色" />

<!-- Child.svelte -->
<h1>你好</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.
考虑使用context而非在共享模块中声明状态。这样可以将状态限定在需要它的应用部分,并且在服务端渲染时可以避免状态在不同用户之间泄露。
请使用
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
    替代
    $:
    赋值和语句(但仅在没有更好方案时使用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:
    指令