Context API
本教程共 50 篇 · 第 33 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
本节目标:理解 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,或者想手动管理键名,可以用 setContext 和 getContext。
<!-- 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. 只在组件初始化时可用
setContext 和 getContext 必须在组件初始化阶段(<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
除了 setContext 和 getContext,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 都能跨组件共享数据,但适用场景不同。
| 对比项 | Context | Store |
|---|---|---|
| 数据流 | 祖先 -> 后代(单向) | 任意方向 |
| 响应式 | 需要配合 $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 适合主题、用户信息等请求级数据