Context上下文 #
一、Context 概述 #
1.1 什么是 Context #
Context 提供了一种在组件树中共享数据的方式,无需逐层传递 props。
text
┌─────────────────────────────────────────────────────────────┐
│ Context 解决的问题 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Props 传递问题 │
│ ┌─────────┐ │
│ │ App │ │
│ │ theme │ │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ Layout │ (不需要 theme) │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ Sidebar │ (不需要 theme) │
│ └────┬────┘ │
│ ↓ props │
│ ┌─────────┐ │
│ │ Button │ ← 需要 theme │
│ └─────────┘ │
│ │
│ Context 解决方案 │
│ ┌─────────────────────────────────────────┐ │
│ │ App (Provider) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Layout │ │ Sidebar │ │ Button │ │ │
│ │ └─────────┘ └─────────┘ │ useContext │ │ │
│ │ └─────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 Context API #
| API | 说明 |
|---|---|
createContext |
创建上下文 |
Provider |
提供上下文值 |
useContext |
消费上下文值 |
二、基本用法 #
2.1 创建 Context #
jsx
import { createContext } from 'solid-js';
// 创建一个 Context,可以提供默认值
const ThemeContext = createContext('light');
const UserContext = createContext(null);
2.2 提供 Context #
jsx
import { createContext } from 'solid-js';
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = createSignal('dark');
return (
<ThemeContext.Provider value={theme()}>
<Layout />
</ThemeContext.Provider>
);
}
2.3 消费 Context #
jsx
import { useContext } from 'solid-js';
function Button() {
const theme = useContext(ThemeContext);
return (
<button class={`btn btn-${theme}`}>
Click me
</button>
);
}
2.4 完整示例 #
jsx
import { createContext, useContext, createSignal } from 'solid-js';
// 1. 创建 Context
const ThemeContext = createContext();
// 2. Provider 组件
function App() {
const [theme, setTheme] = createSignal('light');
const toggleTheme = () => {
setTheme(t => t === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<Layout />
</ThemeContext.Provider>
);
}
// 3. 消费 Context
function Layout() {
return (
<div>
<Header />
<Main />
</div>
);
}
function Header() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<header class={theme()}>
<button onClick={toggleTheme}>
Toggle Theme
</button>
</header>
);
}
function Main() {
const { theme } = useContext(ThemeContext);
return (
<main class={theme()}>
<Button>Click me</Button>
</main>
);
}
function Button(props) {
const { theme } = useContext(ThemeContext);
return (
<button class={`btn btn-${theme()}`}>
{props.children}
</button>
);
}
三、Context 模式 #
3.1 状态管理模式 #
jsx
// contexts/CounterContext.jsx
import { createContext, useContext, createSignal } from 'solid-js';
const CounterContext = createContext();
export function CounterProvider(props) {
const [count, setCount] = createSignal(0);
const value = {
count,
increment: () => setCount(c => c + 1),
decrement: () => setCount(c => c - 1),
reset: () => setCount(0)
};
return (
<CounterContext.Provider value={value}>
{props.children}
</CounterContext.Provider>
);
}
export function useCounter() {
const context = useContext(CounterContext);
if (!context) {
throw new Error('useCounter must be used within CounterProvider');
}
return context;
}
jsx
// App.jsx
import { CounterProvider } from './contexts/CounterContext';
function App() {
return (
<CounterProvider>
<Counter />
<Controls />
</CounterProvider>
);
}
function Counter() {
const { count } = useCounter();
return <p>Count: {count()}</p>;
}
function Controls() {
const { increment, decrement, reset } = useCounter();
return (
<div>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
<button onClick={increment}>+</button>
</div>
);
}
3.2 主题模式 #
jsx
// contexts/ThemeContext.jsx
import { createContext, useContext, createSignal, createMemo } from 'solid-js';
const ThemeContext = createContext();
const themes = {
light: {
background: '#ffffff',
text: '#000000',
primary: '#007bff'
},
dark: {
background: '#1a1a1a',
text: '#ffffff',
primary: '#4da3ff'
}
};
export function ThemeProvider(props) {
const [themeName, setThemeName] = createSignal(
localStorage.getItem('theme') || 'light'
);
const theme = createMemo(() => themes[themeName()]);
const toggleTheme = () => {
setThemeName(t => {
const newTheme = t === 'light' ? 'dark' : 'light';
localStorage.setItem('theme', newTheme);
return newTheme;
});
};
return (
<ThemeContext.Provider value={{ theme, themeName, toggleTheme }}>
{props.children}
</ThemeContext.Provider>
);
}
export function useTheme() {
return useContext(ThemeContext);
}
jsx
// 使用
function App() {
return (
<ThemeProvider>
<ThemedApp />
</ThemeProvider>
);
}
function ThemedApp() {
const { theme } = useTheme();
return (
<div style={{
'background-color': theme().background,
color: theme().text
}}>
<Header />
<Main />
</div>
);
}
function ThemeToggle() {
const { themeName, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Switch to {themeName() === 'light' ? 'Dark' : 'Light'} Mode
</button>
);
}
3.3 用户认证模式 #
jsx
// contexts/AuthContext.jsx
import { createContext, useContext, createSignal, createEffect } from 'solid-js';
const AuthContext = createContext();
export function AuthProvider(props) {
const [user, setUser] = createSignal(null);
const [loading, setLoading] = createSignal(true);
createEffect(() => {
// 检查本地存储的 token
const token = localStorage.getItem('token');
if (token) {
fetchUser(token).then(userData => {
setUser(userData);
setLoading(false);
});
} else {
setLoading(false);
}
});
const login = async (credentials) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
});
const { token, user } = await response.json();
localStorage.setItem('token', token);
setUser(user);
};
const logout = () => {
localStorage.removeItem('token');
setUser(null);
};
return (
<AuthContext.Provider value={{ user, loading, login, logout }}>
{props.children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}
jsx
// 使用
function App() {
return (
<AuthProvider>
<Routes />
</AuthProvider>
);
}
function Routes() {
const { user, loading } = useAuth();
return (
<Show when={!loading()} fallback={<p>Loading...</p>}>
<Switch>
<Match when={user()}>
<Dashboard />
</Match>
<Match when={!user()}>
<LoginPage />
</Match>
</Switch>
</Show>
);
}
function LoginPage() {
const { login } = useAuth();
const handleSubmit = async (e) => {
e.preventDefault();
await login({ username, password });
};
return <form onSubmit={handleSubmit}>...</form>;
}
function Dashboard() {
const { user, logout } = useAuth();
return (
<div>
<h1>Welcome, {user().name}</h1>
<button onClick={logout}>Logout</button>
</div>
);
}
四、嵌套 Context #
4.1 多层 Context #
jsx
function App() {
return (
<ThemeProvider>
<AuthProvider>
<NotificationProvider>
<Router>
<Routes />
</Router>
</NotificationProvider>
</AuthProvider>
</ThemeProvider>
);
}
4.2 Context 组合 #
jsx
function useAppContext() {
const theme = useTheme();
const auth = useAuth();
const notifications = useNotifications();
return { theme, auth, notifications };
}
function MyComponent() {
const { theme, auth, notifications } = useAppContext();
// ...
}
4.3 覆盖 Context 值 #
jsx
function App() {
return (
<ThemeContext.Provider value="light">
<Layout />
<ThemeContext.Provider value="dark">
<Sidebar /> {/* 使用 dark 主题 */}
</ThemeContext.Provider>
</ThemeContext.Provider>
);
}
五、高级用法 #
5.1 带默认值的 Context #
jsx
const ThemeContext = createContext({
theme: () => 'light',
toggleTheme: () => {}
});
function MaybeWithProvider() {
// 如果没有 Provider,使用默认值
const { theme } = useContext(ThemeContext);
return <p>Theme: {theme()}</p>;
}
5.2 动态 Context #
jsx
function createDynamicContext(factory) {
const Context = createContext();
return {
Provider: (props) => {
const value = factory(props);
return (
<Context.Provider value={value}>
{props.children}
</Context.Provider>
);
},
useContext: () => useContext(Context)
};
}
// 使用
const UserContext = createDynamicContext((props) => {
const [user, setUser] = createSignal(props.initialUser);
return { user, setUser };
});
5.3 Context with Selector #
jsx
function useContextSelector(Context, selector) {
const context = useContext(Context);
return createMemo(() => selector(context));
}
// 使用
function UserName() {
const name = useContextSelector(UserContext, ctx => ctx.user()?.name);
return <p>{name()}</p>;
}
六、最佳实践 #
6.1 Context 文件组织 #
text
src/
├── contexts/
│ ├── ThemeContext.jsx
│ ├── AuthContext.jsx
│ └── NotificationContext.jsx
├── components/
│ └── ...
└── App.jsx
6.2 自定义 Hook 封装 #
jsx
// contexts/ThemeContext.jsx
const ThemeContext = createContext();
export function ThemeProvider(props) {
// ...
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
6.3 避免过度使用 #
jsx
// 不推荐:所有状态都放 Context
function App() {
return (
<StateProvider>
<App />
</StateProvider>
);
}
// 推荐:只在需要跨组件共享时使用
function App() {
return (
<AuthProvider>
<ThemeProvider>
<App />
</ThemeProvider>
</AuthProvider>
);
}
6.4 性能优化 #
jsx
// 拆分 Context 减少不必要的渲染
function App() {
return (
<UserProvider>
<ThemeProvider>
<NotificationProvider>
<App />
</NotificationProvider>
</ThemeProvider>
</UserProvider>
);
}
// 使用 selector 避免订阅整个 context
function UserName() {
const { user } = useAuth();
// 只订阅 user,不订阅其他 auth 状态
return <span>{user()?.name}</span>;
}
七、总结 #
7.1 Context API #
| API | 说明 |
|---|---|
createContext(defaultValue) |
创建上下文 |
Context.Provider |
提供上下文值 |
useContext(Context) |
消费上下文值 |
7.2 使用场景 #
| 场景 | 是否使用 Context |
|---|---|
| 主题切换 | ✅ 推荐 |
| 用户认证 | ✅ 推荐 |
| 国际化 | ✅ 推荐 |
| 父子组件通信 | ❌ 使用 Props |
| 兄弟组件通信 | ✅ 可以使用 |
| 全局状态 | ✅ 推荐 |
7.3 最佳实践 #
- 合理拆分:按功能拆分多个 Context
- 封装 Hook:提供 useXxx 函数
- 错误处理:检查 Context 是否存在
- 默认值:提供合理的默认值
- 性能优化:避免不必要的订阅
最后更新:2026-03-28