掌握个性化的艺术:构建 React 与 Tailwind 主题切换器
Source: Dev.to

为什么在真实项目中这很重要
想象一下,你正在构建下一个大型社交媒体应用。你已经有了时尚的个人资料页、直观的动态流和充满活力的社区。但随后,用户开始询问:“能不能提供暗黑模式?”
一个结构良好的主题切换器可以解决这个问题。我们将利用 React 的 Context API 创建一个全局主题状态,任何组件都可以访问,并使用 Tailwind CSS 的 dark: 变体轻松应用特定主题的样式。我们还会把用户的选择持久化到 localStorage,这样在后续访问时能够记住他们的偏好。
让我们动手实现
步骤 1:搭建 React 与 Tailwind 项目
npx create-react-app theme-switcher-app
cd theme-switcher-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
在 tailwind.config.js 中启用暗黑模式:
// tailwind.config.js
module.exports = {
darkMode: 'class', // 我们将在 HTML 元素上切换 'dark' 类
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [],
}
步骤 2:创建主题 Context
// src/contexts/ThemeContext.tsx
import React, { createContext, useState, useEffect, useContext } from 'react';
type Theme = 'light' | 'dark';
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext(undefined);
export const ThemeProvider: React.FC = ({ children }) => {
const [theme, setTheme] = useState(() => {
// 先检查 localStorage,再检查系统偏好
const stored = localStorage.getItem('theme') as Theme;
if (stored) return stored;
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
});
useEffect(() => {
const root = document.documentElement;
root.classList.remove('light', 'dark');
root.classList.add(theme);
localStorage.setItem('theme', theme);
}, [theme]);
const toggleTheme = () => {
setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
};
return (
{children}
);
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
步骤 3:构建主题切换组件
// src/components/ThemeSwitcher.tsx
import { useTheme } from '../contexts/ThemeContext';
const ThemeSwitcher = () => {
const { theme, toggleTheme } = useTheme();
return (
{theme === 'light' ? '🌙' : '☀️'}
);
};
export default ThemeSwitcher;
步骤 4:整合一切
// src/App.tsx
import { ThemeProvider } from './contexts/ThemeContext';
import ThemeSwitcher from './components/ThemeSwitcher';
function App() {
return (
<ThemeProvider>
<div className="min-h-screen flex flex-col items-center justify-center bg-white dark:bg-gray-900 transition-colors duration-300">
<header className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">
Welcome to My Themed App
</h1>
</header>
<main className="text-center">
<ThemeSwitcher />
<p className="mt-4 text-gray-700 dark:text-gray-300">
Toggle the theme and watch the magic happen.
</p>
</main>
</div>
</ThemeProvider>
);
}
export default App;
大多数教程忽略的点
- 优先尊重系统偏好 —— Context 会先检查
window.matchMedia('(prefers-color-scheme: dark)'),再决定默认值,让应用更贴合原生体验。 - 添加平滑过渡 ——
transition-colors duration-300类可以避免切换时的突兀感。
性能考虑
在使用服务器端渲染(例如 Next.js)时,可能会出现未样式化内容的闪烁,因为 localStorage 在服务器上不可用。可以通过在 <head> 中添加一段小脚本,在 React 水合之前运行来缓解:
<script>
const theme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.classList.add(theme);
</script>
关键要点
- 使用 React Context 管理全局主题状态 —— 比 prop drilling 更简洁。
- Tailwind 的
dark:变体让主题化几乎毫不费力。 - 将偏好持久化到
localStorage,为回访用户保留设置。 - 将系统偏好设为初始默认值。
- 添加平滑过渡,提升精致感。
现在就去实现,让你的用户拥有自行选择主题的权力吧。他们一定会感激你的。