首页 / Svelte 5 入门教程 / 组件通信方式汇总

Svelte 5 入门教程

组件通信方式汇总

本教程共 50 篇 · 第 37 篇 · 更新于 2026-08-05 · 约 6 分钟阅读

SvelteSvelte 5组件通信PropsContextStore

本节目标:一次性理清 Svelte 5 的六种组件通信方式,知道每种方式适合什么场景,遇到实际需求能快速选对方案。

为什么需要这么多方式

组件不会孤立存在,它们需要交换数据。父组件要把数据传给子组件,子组件要通知父组件发生了事件,深层嵌套的组件要共享状态。不同的场景需要不同的通信手段,选对了代码简洁,选错了到处打补丁。

Svelte 5 提供了以下几种方式:

方式数据流向适用场景
Props父 → 子传递数据给直接子组件
回调 Props子 → 父子组件通知父组件
$bindable父 ⇌ 子双向绑定表单值
Context祖先 → 后代跨多层传递,避免逐层传
Store全局复杂异步数据流
全局 $state全局简单共享状态

Props:父传子

最基础的通信方式。父组件通过属性把数据传给子组件,子组件用 $props() 接收:

<!-- App.svelte -->
<script>
	import Greeting from './Greeting.svelte';
</script>

<Greeting name="张三" />
<!-- Greeting.svelte -->
<script>
	let { name } = $props();
</script>

<h1>你好,{name}!</h1>

Props 可以设默认值,也可以重命名:

// 默认值
let { count = 0 } = $props();

// 重命名(比如 prop 名是 JS 关键字)
let { class: className = '' } = $props();

// 收集剩余 props
let { id, ...rest } = $props();
Note

Props 是单向的:父变则子更新,但子组件不应该直接修改传入的 prop。需要修改时用回调 props 或 $bindable

回调 Props:子传父

子组件不能直接修改父组件的数据,但可以调用父组件传下来的函数来”通知”:

<!-- App.svelte -->
<script>
	import Counter from './Counter.svelte';

	let total = $state(0);

	function handleIncrement() {
		total++;
	}
</script>

<Counter onIncrement={handleIncrement} />
<p>总计:{total}</p>
<!-- Counter.svelte -->
<script>
	let { onIncrement } = $props();
</script>

<button onclick={onIncrement}>+1</button>

父组件把 handleIncrement 函数作为 onIncrement prop 传下去。子组件点击按钮时调用它,父组件的 total 就会增加。

Tip

这种模式本质上和 React 的回调 props 一样。命名习惯是用 on 开头,如 onChangeonSubmitonSelect

$bindable:双向绑定

有时候父和子需要共享同一个值,父子两边都能改。用 $bindable 声明一个可绑定的 prop:

<!-- FancyInput.svelte -->
<script>
	let { value = $bindable() } = $props();
</script>

<input bind:value={value} />

父组件用 bind: 指令绑定:

<!-- App.svelte -->
<script>
	import FancyInput from './FancyInput.svelte';

	let name = $state('张三');
</script>

<FancyInput bind:value={name} />
<p>你输入了:{name}</p>

子组件里的 <input> 改了值,父组件的 name 同步更新;父组件改了 name,子组件的 input 也同步更新。

Note

$bindable 要谨慎使用。过度使用双向绑定会让数据流向难以追踪。大多数场景用回调 props 更清晰,只有在表单控件这类”值天然需要同步”的场景才推荐用 bind:

Context:跨层传递

当组件嵌套很深时,逐层传 props 很痛苦(叫”prop drilling”)。Context 让祖先组件把数据”挂”到组件树上,任意层级的后代都能直接取:

// context.ts
import { createContext } from 'svelte';

interface User {
	name: string;
}

export const [getUserContext, setUserContext] = createContext<User>();
<!-- Parent.svelte -->
<script>
	import { setUserContext } from './context';
	let { children } = $props();

	setUserContext({ name: '张三' });
</script>

{@render children()}
<!-- DeepChild.svelte -->
<script>
	import { getUserContext } from './context';

	const user = getUserContext();
</script>

<p>你好 {user.name}</p>

Context 的特点:

  • 只能从祖先传给后代,不能逆流
  • 不需要逐层传递,中间组件甚至不知道有这个数据
  • 可以存响应式状态($state 对象),后代能感知变化
Note

Svelte 5.40 起推荐用 createContext 替代旧的 setContext/getContext,类型更安全。如果你的版本低于 5.40,用 setContext(key, value)getContext(key)

Context 中存响应式状态时要注意:修改对象的属性可以,但重新赋值整个对象会断开链接:

// 正确:修改属性
counter.count = 0;

// 错误:重新赋值会断开引用
counter = { count: 0 };

Store:全局状态

Store 是 Svelte 的传统响应式方案。在 Svelte 5 中,大部分场景可以用全局 $state 替代,但处理复杂异步流时 Store 仍然好用:

// store.js
import { writable } from 'svelte/store';

export const theme = writable('light');
<script>
	import { theme } from './store.js';
</script>

<!-- $ 前缀自动订阅 -->
<p>当前主题:{$theme}</p>

<button onclick={() => $theme = 'dark'}>切换暗色</button>

$ 前缀会自动订阅和取消订阅,赋值时调用 store 的 set 方法。

全局 $state:简单共享

Svelte 5 中最简单的全局状态方案。把 $state 放在 .svelte.js 文件里导出,任何组件都能用:

// state.svelte.ts
export const userState = $state({
	name: '张三',
	age: 25
});
<script>
	import { userState } from './state.svelte.ts';
</script>

<p>{userState.name},{userState.age} 岁</p>
<button onclick={() => userState.age++}>长大一岁</button>
Tip

简单的全局状态用 $state 就够了。只有当你需要复杂的数据流(如防抖、节流、多源合并)时,才考虑用 Store。

如何选择

用一个流程来帮你决策:

  1. 父子直接通信 → Props + 回调 Props
  2. 表单控件双向同步$bindable
  3. 跨 3 层以上传递 → Context
  4. 多个不相关组件共享状态 → 全局 $state
  5. 复杂异步数据流 → Store
Note

别把简单问题搞复杂。两个相邻组件之间通信,用 Props 就行,别上 Context 或 Store。选最简单能解决问题的方案。

本节回顾

  • Props 是基础通信方式,父传子,用 $props() 接收
  • 回调 Props 让子组件通过函数调用通知父组件
  • $bindable 实现双向绑定,适合表单控件场景
  • Context 用 createContext 创建,解决跨层传递问题
  • Store 适合复杂异步流,简单场景用全局 $state 即可
  • 选择原则:越简单越好,能用 Props 就别用 Context