首页 / Svelte 5 入门教程 / Context API

Svelte 5 入门教程

Context API

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

SvelteSvelte 5Context APIsetContextgetContext组件通信

本节目标:理解 Context API 解决的问题,学会用 createContext 和 setContext/getContext 传递数据,掌握 Context 与 Store 的区别和选择策略。

为什么需要 Context

组件之间传数据,最直接的方式是 Props。但如果数据要穿过很多层组件,就得每层都写一遍 Prop——这叫”prop 钻孔”(prop drilling),非常烦人。

打个比方,你给住在一楼的朋友送东西,但门卫不让你进去,你得把东西交给门卫,门卫交给前台,前台交给保安,保安才送到你朋友手里。Context API 就像给你一张门禁卡,直接进去找朋友。

Context 让祖先组件设置数据,任意层级的后代组件都能直接获取,不用一层层传 Props。

createContext(推荐写法)

从 Svelte 5.40 开始,推荐用 createContext 创建一组类型安全的 getter/setter 对。

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

interface User {
	name: string;
}

export const [getUserContext, setUserContext] = createContext<User>();

在祖先组件中设置 Context:

<!-- Parent.svelte -->
<script>
	import { setUserContext } from './context';
	import Child from './Child.svelte';

	let { children } = $props();

	// 设置 Context,后代组件可以读取
	setUserContext({ name: '码上学' });
</script>

{@render children()}

在后代组件中获取 Context:

<!-- Child.svelte -->
<script>
	import { getUserContext } from './context';

	const user = getUserContext();
</script>

<h1>你好 {user.name},来自 Child 组件</h1>
Note

createContext 提供了类型安全,不需要手动管理 key。这是 Svelte 5.40+ 的推荐写法。如果你用更早的版本,需要用 setContext/getContext(下面讲)。

setContext / getContext

如果你用的 Svelte 版本低于 5.40,或者想手动管理键名,可以用 setContextgetContext

<!-- Parent.svelte -->
<script>
	import { setContext } from 'svelte';

	setContext('my-context', '来自 Parent 的问候');
</script>
<!-- Child.svelte -->
<script>
	import { getContext } from 'svelte';

	const message = getContext('my-context');
</script>

<h1>{message},来自 Child 组件</h1>

键的设计

setContext(key, value)key 可以是任何 JavaScript 值。推荐用 Symbol 或 class 来避免冲突。

// 用 Symbol 做键,绝对不冲突
const userKey = Symbol('user');

// 祖先
setContext(userKey, { name: '码上学' });

// 后代
const user = getContext(userKey);
// 用 class 做键,也有类型安全
class UserContext {
	name: string;
	constructor(name: string) {
		this.name = name;
	}
}

// 祖先
setContext(UserContext, new UserContext('码上学'));

// 后代
const user = getContext(UserContext);
Tip

用 class 做键的好处是类型推断更友好——TypeScript 知道 getContext(UserContext) 返回的是 UserContext 实例。

Context 的特点

Context 有几个重要特点你需要了解:

1. 只在组件初始化时可用

setContextgetContext 必须在组件初始化阶段(<script> 顶层)调用,不能在事件处理器或 $effect 中调用。

2. 不是响应式的

Context 本身不是响应式的。你存进去一个普通值,后代读到的就是那个值。如果需要响应式,可以把 $state 对象放进 Context。

3. 组件级隔离

每个组件实例有自己独立的 Context。如果同一组件被多次使用,每个实例的 Context 互不影响。

Context + 响应式状态

$state 对象放入 Context,后代组件就能共享响应式状态。

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

interface Counter {
	count: number;
}

export const [getCounter, setCounter] = createContext<Counter>();
<!-- App.svelte -->
<script>
	import { setCounter } from './context';
	import Child from './Child.svelte';

	let counter = $state({ count: 0 });

	setCounter(counter);
</script>

<button onclick={() => counter.count += 1}>
	加一(App 里点)
</button>

<Child />
<Child />
<Child />

<button onclick={() => counter.count = 0}>重置</button>
<!-- Child.svelte -->
<script>
	import { getCounter } from './context';

	const counter = getCounter();
</script>

<p>{counter.count}</p>

三个 Child 组件会同步显示同一个计数值。

Note

如果用 counter = { count: 0 } 重新赋值整个对象,会”断开链接”——后代组件持有的还是旧对象的引用。应该用 counter.count = 0 修改属性而不是重新赋值。Svelte 会在你犯错时发出警告。

其他 Context API

除了 setContextgetContext,Svelte 还提供:

  • hasContext(key):检查 Context 是否存在
  • getAllContexts():获取当前组件的所有 Context
<script>
	import { hasContext, getContext, getAllContexts } from 'svelte';

	if (hasContext('theme')) {
		const theme = getContext('theme');
		console.log('主题是:', theme);
	}

	// 获取所有 Context(返回一个 Map)
	const all = getAllContexts();
</script>

Context vs Store

Context 和 Store 都能跨组件共享数据,但适用场景不同。

对比项ContextStore
数据流祖先 -> 后代(单向)任意方向
响应式需要配合 $state自带响应式
可访问性只有后代能访问任何引入的代码都能访问
SSR 安全安全(请求间隔离)需注意全局状态污染
订阅机制无订阅,直接读订阅-通知
适合场景主题、用户信息、配置全局状态、异步数据

SSR 安全性

在服务端渲染时,如果你把共享状态放在模块级别的变量里,不同请求之间可能串数据。

// 危险!SSR 时多个请求共享同一个对象
export const globalState = $state({ user: null });

Context 在组件实例级别隔离,不会有这个问题。每个请求的组件树是独立的,Context 也是独立的。

Tip

如果你的应用需要 SSR,用 Context 来传递请求相关的数据(如当前用户、主题设置)比用全局模块变量安全得多。

实战:主题切换

一个经典的 Context 应用场景——主题切换。

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

interface Theme {
	mode: 'light' | 'dark';
	toggle: () => void;
}

export const [getTheme, setTheme] = createContext<Theme>();
<!-- ThemeProvider.svelte -->
<script>
	import { setTheme } from './theme';

	let { children } = $props();

	let mode = $state('light');

	setTheme({
		get mode() { return mode; },
		toggle() {
			mode = mode === 'light' ? 'dark' : 'light';
		}
	});
</script>

<div class={mode}>
	{@render children?.()}
</div>

<style>
	.light { background: white; color: black; }
	.dark { background: #1a1a1a; color: white; }
</style>
<!-- ThemeButton.svelte -->
<script>
	import { getTheme } from './theme';

	const theme = getTheme();
</script>

<button onclick={theme.toggle}>
	切换到 {theme.mode === 'light' ? '暗色' : '亮色'} 模式
</button>

任何在 ThemeProvider 内部的组件都能通过 getTheme() 获取主题并切换。

本节回顾

  • Context API 解决了”prop 钻孔”问题,让祖先组件的数据直达后代组件
  • Svelte 5.40+ 推荐用 createContext() 创建类型安全的 getter/setter 对
  • setContext(key, value)getContext(key) 是传统写法,key 推荐 Symbol 或 class
  • Context 不是响应式的,需要配合 $state 才能实现响应式共享
  • 修改 Context 中的状态对象要用属性赋值,不要整体重新赋值
  • Context 在组件实例级别隔离,SSR 时请求间不会串数据
  • Store 适合全局状态和异步数据,Context 适合主题、用户信息等请求级数据