首页 / Svelte 5 入门教程 / 双向绑定 $bindable

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: 回传)
  1. 父组件的 message 通过 props 传给子组件的 value
  2. 子组件内部修改 value(通过 bind:value
  3. 修改同步回父组件的 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: 右侧必须是可赋值的变量