Svelte 5 入门教程
双向绑定 $bindable
本教程共 50 篇 · 第 11 篇 · 更新于 2026-08-05 · 约 5 分钟阅读
SvelteSvelte 5Runes$bindable双向绑定
11. 双向绑定 $bindable
本节目标:掌握 $bindable 的用法,理解双向绑定的数据流和使用场景。学完你能在父子组件间实现双向数据同步。
单向 vs 双向
Svelte 的 props 默认是单向的:父传子,数据从上往下流。这让数据流向清晰可预测。
但有时候你确实需要数据双向流动——子组件修改值时,父组件同步更新。这就是 $bindable 的用途。
打个比方,单向 props 就像广播——电台播什么你听什么。双向绑定更像打电话——双方都能说也能听。
$bindable 基本用法
在子组件中用 $bindable() 标记一个 prop 为可绑定:
<!--- file: FancyInput.svelte --->
<script>
let { value = $bindable(), ...props } = $props();
</script>
<input bind:value={value} {...props} />
<style>
input {
font-family: 'Comic Sans MS';
color: deeppink;
}
</style>
父组件用 bind: 指令绑定这个 prop:
<!--- file: App.svelte --->
<script>
import FancyInput from './FancyInput.svelte';
let message = $state('hello');
</script>
<FancyInput bind:value={message} />
<p>你输入了:{message}</p>
现在在 input 中输入文字时,message 会同步更新,<p> 里的内容也跟着变。
$bindable 的工作原理
数据流是这样的:
父组件 message ──→ 子组件 value(props 传入)
子组件 value ──→ 父组件 message(bind: 回传)
- 父组件的
message通过 props 传给子组件的value - 子组件内部修改
value(通过bind:value) - 修改同步回父组件的
message
默认值
$bindable() 可以接受一个默认值参数。当父组件没有用 bind: 绑定时,使用这个默认值:
<script>
// 父组件不用 bind: 时,value 默认为 'fallback'
let { value = $bindable('fallback') } = $props();
</script>
<input bind:value={value} />
<!-- 用了 bind:,默认值被忽略 -->
<FancyInput bind:value={message} />
<!-- 没用 bind:,使用默认值 'fallback' -->
<FancyInput />
Note默认值只在父组件完全不传该 prop 时生效。如果父组件传了普通 prop(不用
bind:),则使用传入的值。
实用场景:表单组件
$bindable 最常见的用途是封装表单控件:
<!--- file: FormField.svelte --->
<script>
let { value = $bindable(), label, type = 'text' } = $props();
</script>
<label>
{label}
<input {type} bind:value />
</label>
<script>
import FormField from './FormField.svelte';
let username = $state('');
let email = $state('');
</script>
<FormField label="用户名" bind:value={username} />
<FormField label="邮箱" type="email" bind:value={email} />
<p>用户名:{username},邮箱:{email}</p>
实用场景:可编辑卡片
<!--- file: EditableCard.svelte --->
<script>
let { title = $bindable(''), content = $bindable('') } = $props();
let editing = $state(false);
</script>
{#if editing}
<input bind:value={title} placeholder="标题" />
<textarea bind:value={content} placeholder="内容"></textarea>
<button onclick={() => editing = false}>完成</button>
{:else}
<h3>{title}</h3>
<p>{content}</p>
<button onclick={() => editing = true}>编辑</button>
{/if}
<script>
import EditableCard from './EditableCard.svelte';
let cardTitle = $state('我的卡片');
let cardContent = $state('这是一段内容');
</script>
<EditableCard bind:title={cardTitle} bind:content={cardContent} />
<p>当前标题:{cardTitle}</p>
使用限制和注意事项
不要滥用
双向绑定虽然方便,但过度使用会让数据流变得难以追踪。能用回调函数(callback props)解决的,优先用回调:
<!-- 方式一:回调函数(推荐,数据流清晰) -->
<script>
let { value, oninput } = $props();
</script>
<input {value} oninput={(e) => oninput(e.target.value)} />
<!-- 方式二:双向绑定(适合简单表单) -->
<script>
let { value = $bindable() } = $props();
</script>
<input bind:value />
不要修改非 bindable 的 props
<script>
let { obj } = $props();
</script>
<!-- ❌ 修改非 bindable 的 prop 属性会触发警告 -->
<button onclick={() => obj.count++}>修改</button>
如果你需要修改,要么用 $bindable,要么用回调通知父组件修改。
bind: 只能绑定到变量
bind: 的右侧必须是一个可赋值的变量,不能是表达式或字面量:
<!-- ✅ 绑定到变量 -->
<FancyInput bind:value={message} />
<!-- ❌ 不能绑定到表达式 -->
<FancyInput bind:value={obj.prop} />
<!-- 虽然 obj.prop 可以工作(如果 obj 是 $state),但不推荐 -->
<!-- ❌ 不能绑定到字面量 -->
<FancyInput bind:value={'hello'} />
Tip如果你需要更复杂的绑定逻辑(比如值变换),Svelte 5 支持函数绑定:
bind:value={() => getter, setter}。这在第 9 章的双向联动示例中有用到。
$bindable vs 回调函数
| 对比项 | $bindable | 回调函数 |
|---|---|---|
| 数据流 | 双向 | 单向(子通过回调通知父) |
| 代码量 | 少(一行搞定) | 多(需写回调逻辑) |
| 可追踪性 | 较差(隐式同步) | 好(显式调用) |
| 适用场景 | 简单表单、直接映射 | 复杂逻辑、多组件联动 |
| Svelte 4 对应 | export let + bind: | createEventDispatcher |
本节回顾
$bindable()标记一个 prop 为可双向绑定- 父组件用
bind:propName={变量}绑定,子组件修改时父组件同步更新 $bindable('默认值')设置未绑定时的默认值- 最适合封装表单控件和可编辑组件
- 不要滥用——能用回调函数就用回调,保持数据流清晰
- 不要修改非
$bindable的 props 属性 bind:右侧必须是可赋值的变量