Context API #
一、Context 概述 #
Context API 用于在组件树中共享数据,避免 Props 逐层传递(prop drilling)。
text
┌─────────────────────────────────────────────────────────────┐
│ Context 工作原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统 Props 传递 │
│ ┌─────────┐ │
│ │ App │ data │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ Header │ │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ Nav │ │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ UserMenu│ ← 需要data │
│ └─────────┘ │
│ │
│ Context 方式 │
│ ┌─────────┐ │
│ │ App │ setContext('data', data) │
│ └────┬────┘ │
│ ↓ │
│ ┌─────────┐ │
│ │ Header │ │
│ └────┬────┘ │
│ ↓ │
│ ┌─────────┐ │
│ │ Nav │ │
│ └────┬────┘ │
│ ↓ │
│ ┌─────────┐ │
│ │ UserMenu│ getContext('data') ← 直接获取 │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
二、基本用法 #
2.1 设置 Context #
svelte
<script>
import { setContext } from 'svelte';
const theme = 'dark';
const user = { name: 'Alice', role: 'admin' };
setContext('theme', theme);
setContext('user', user);
</script>
<slot />
2.2 获取 Context #
svelte
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
const user = getContext('user');
</script>
<p>Theme: {theme}</p>
<p>User: {user.name}</p>
2.3 完整示例 #
ThemeProvider.svelte:
svelte
<script>
import { setContext } from 'svelte';
let theme = $state('light');
setContext('theme', {
get value() { return theme; },
set value(v) { theme = v; },
toggle: () => {
theme = theme === 'light' ? 'dark' : 'light';
}
});
</script>
<div data-theme={theme}>
<slot />
</div>
<style>
[data-theme="light"] {
--bg: #ffffff;
--text: #333333;
--primary: #ff3e00;
}
[data-theme="dark"] {
--bg: #1a1a1a;
--text: #ffffff;
--primary: #ff6e40;
}
</style>
ThemeButton.svelte:
svelte
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<button onclick={() => theme.toggle()}>
{theme.value === 'light' ? '🌙 Dark' : '☀️ Light'}
</button>
App.svelte:
svelte
<script>
import ThemeProvider from './ThemeProvider.svelte';
import ThemeButton from './ThemeButton.svelte';
import Content from './Content.svelte';
</script>
<ThemeProvider>
<header>
<h1>My App</h1>
<ThemeButton />
</header>
<Content />
</ThemeProvider>
三、Context 特点 #
3.1 作用域 #
Context 只在设置它的组件的后代组件中可用:
svelte
<script>
import { setContext, getContext } from 'svelte';
import Child from './Child.svelte';
setContext('message', 'Hello from Parent');
const msg = getContext('message');
console.log(msg);
</script>
<Child />
3.2 键名建议 #
使用 Symbol 作为键名避免冲突:
javascript
const THEME_KEY = Symbol('theme');
const USER_KEY = Symbol('user');
const CONFIG_KEY = Symbol('config');
svelte
<script>
import { setContext } from 'svelte';
const THEME_KEY = Symbol('theme');
setContext(THEME_KEY, 'dark');
</script>
3.3 默认值处理 #
svelte
<script>
import { getContext } from 'svelte';
const theme = getContext('theme') || 'light';
</script>
四、响应式 Context #
4.1 使用 Getter #
svelte
<script>
import { setContext } from 'svelte';
let count = $state(0);
setContext('counter', {
get count() { return count; },
increment: () => { count += 1; },
decrement: () => { count -= 1; }
});
</script>
<slot />
4.2 使用 Store #
svelte
<script>
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
const theme = writable('light');
setContext('theme', theme);
</script>
<slot />
子组件:
svelte
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<button onclick={() => theme.set($theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
4.3 使用类 #
svelte
<script>
import { setContext } from 'svelte';
class ThemeManager {
theme = $state('light');
toggle() {
this.theme = this.theme === 'light' ? 'dark' : 'light';
}
}
const manager = new ThemeManager();
setContext('theme', manager);
</script>
<slot />
五、实际应用示例 #
5.1 表单 Context #
Form.svelte:
svelte
<script>
import { setContext } from 'svelte';
let values = $state({});
let errors = $state({});
let touched = $state({});
function setField(name, value) {
values = { ...values, [name]: value };
}
function setTouched(name) {
touched = { ...touched, [name]: true };
}
function setError(name, error) {
errors = { ...errors, [name]: error };
}
function validate() {
return Object.keys(errors).length === 0;
}
setContext('form', {
get values() { return values; },
get errors() { return errors; },
get touched() { return touched; },
setField,
setTouched,
setError,
validate
});
</script>
<form>
<slot />
</form>
FormField.svelte:
svelte
<script>
import { getContext } from 'svelte';
let { name, label, type = 'text' } = $props();
const form = getContext('form');
let value = $derived(form.values[name] || '');
let error = $derived(form.errors[name]);
let isTouched = $derived(form.touched[name]);
</script>
<div class="form-field">
<label>{label}</label>
<input
type={type}
bind:value
onblur={() => form.setTouched(name)}
oninput={() => form.setField(name, value)}
/>
{#if isTouched && error}
<span class="error">{error}</span>
{/if}
</div>
使用:
svelte
<script>
import Form from './Form.svelte';
import FormField from './FormField.svelte';
</script>
<Form>
<FormField name="email" label="Email" type="email" />
<FormField name="password" label="Password" type="password" />
<button type="submit">Submit</button>
</Form>
5.2 导航 Context #
Navigation.svelte:
svelte
<script>
import { setContext } from 'svelte';
let items = [];
let activeId = $state(null);
function register(id) {
items = [...items, id];
if (!activeId) activeId = id;
return items.indexOf(id);
}
function unregister(id) {
items = items.filter(i => i !== id);
}
function activate(id) {
activeId = id;
}
setContext('navigation', {
register,
unregister,
activate,
get activeId() { return activeId; }
});
</script>
<nav>
<slot />
</nav>
NavItem.svelte:
svelte
<script>
import { getContext, onMount } from 'svelte';
let { id, href } = $props();
const nav = getContext('navigation');
onMount(() => {
nav.register(id);
return () => nav.unregister(id);
});
</script>
<a
href={href}
class:active={nav.activeId === id}
onclick={() => nav.activate(id)}
>
<slot />
</a>
5.3 国际化 Context #
I18n.svelte:
svelte
<script>
import { setContext } from 'svelte';
const translations = {
en: {
hello: 'Hello',
goodbye: 'Goodbye',
submit: 'Submit'
},
zh: {
hello: '你好',
goodbye: '再见',
submit: '提交'
}
};
let locale = $state('en');
function t(key) {
return translations[locale]?.[key] || key;
}
function setLocale(newLocale) {
locale = newLocale;
}
setContext('i18n', {
get locale() { return locale; },
t,
setLocale
});
</script>
<slot />
Translate.svelte:
svelte
<script>
import { getContext } from 'svelte';
let { key } = $props();
const i18n = getContext('i18n');
</script>
<span>{i18n.t(key)}</span>
使用:
svelte
<script>
import I18n from './I18n.svelte';
import Translate from './Translate.svelte';
</script>
<I18n>
<p><Translate key="hello" /></p>
<p><Translate key="goodbye" /></p>
<button><Translate key="submit" /></button>
</I18n>
六、Context vs Store #
6.1 对比 #
| 特性 | Context | Store |
|---|---|---|
| 作用域 | 组件树 | 全局 |
| 响应式 | 需要额外处理 | 内置 |
| 适用场景 | 局部共享 | 全局状态 |
| 访问方式 | getContext | $ 语法 |
6.2 选择建议 #
text
使用 Context:
├── 主题、配置等局部共享
├── 表单状态
├── 组件库内部通信
└── 避免全局污染
使用 Store:
├── 用户信息
├── 全局通知
├── 购物车
└── 跨页面共享状态
七、最佳实践 #
7.1 封装 Context #
javascript
export const ThemeKey = Symbol('theme');
export function createThemeContext() {
let theme = $state('light');
return {
get theme() { return theme; },
set theme(v) { theme = v; },
toggle: () => {
theme = theme === 'light' ? 'dark' : 'light';
}
};
}
svelte
<script>
import { setContext } from 'svelte';
import { ThemeKey, createThemeContext } from './theme-context.js';
setContext(ThemeKey, createThemeContext());
</script>
<slot />
7.2 类型安全 #
svelte
<script lang="ts">
import { setContext, getContext } from 'svelte';
interface ThemeContext {
theme: 'light' | 'dark';
toggle: () => void;
}
declare const THEME_KEY: unique symbol;
setContext<THEME_KEY, ThemeContext>(THEME_KEY, {
theme: 'light',
toggle: () => {}
});
const ctx = getContext<THEME_KEY, ThemeContext>(THEME_KEY);
</script>
7.3 错误处理 #
svelte
<script>
import { getContext } from 'svelte';
function useTheme() {
const theme = getContext('theme');
if (!theme) {
throw new Error('useTheme must be used within ThemeProvider');
}
return theme;
}
const theme = useTheme();
</script>
八、完整示例:标签页组件 #
Tabs.svelte:
svelte
<script>
import { setContext } from 'svelte';
let { defaultTab = 0 } = $props();
let activeTab = $state(defaultTab);
function selectTab(index) {
activeTab = index;
}
setContext('tabs', {
get activeTab() { return activeTab; },
selectTab
});
</script>
<div class="tabs">
<slot />
</div>
TabList.svelte:
svelte
<script>
import { getContext } from 'svelte';
const tabs = getContext('tabs');
</script>
<div class="tab-list" role="tablist">
<slot />
</div>
Tab.svelte:
svelte
<script>
import { getContext } from 'svelte';
let { index } = $props();
const tabs = getContext('tabs');
</script>
<button
role="tab"
aria-selected={tabs.activeTab === index}
class:active={tabs.activeTab === index}
onclick={() => tabs.selectTab(index)}
>
<slot />
</button>
TabPanel.svelte:
svelte
<script>
import { getContext } from 'svelte';
let { index } = $props();
const tabs = getContext('tabs');
</script>
{#if tabs.activeTab === index}
<div role="tabpanel" class="tab-panel">
<slot />
</div>
{/if}
使用:
svelte
<script>
import Tabs from './Tabs.svelte';
import TabList from './TabList.svelte';
import Tab from './Tab.svelte';
import TabPanel from './TabPanel.svelte';
</script>
<Tabs>
<TabList>
<Tab index={0}>Tab 1</Tab>
<Tab index={1}>Tab 2</Tab>
<Tab index={2}>Tab 3</Tab>
</TabList>
<TabPanel index={0}>Content 1</TabPanel>
<TabPanel index={1}>Content 2</TabPanel>
<TabPanel index={2}>Content 3</TabPanel>
</Tabs>
九、总结 #
| 函数 | 说明 |
|---|---|
setContext(key, value) |
设置 Context |
getContext(key) |
获取 Context |
hasContext(key) |
检查 Context 是否存在 |
Context API 要点:
- 使用
setContext设置,getContext获取 - Context 只在后代组件中可用
- 使用 Symbol 作为键名避免冲突
- 结合响应式实现动态更新
- 适合组件树内部共享数据
- 与 Store 配合处理全局状态
最后更新:2026-03-28