为了对比 React 和 Vue 在相同项目上的开发体验,我决定从零开始同时手撸 React 和 Vue 后台
代码仓库会和本系列文章同时更新,欢迎 fork,感谢 star🌟
React | Vue | |
---|---|---|
仓库 | github.com/Levix0501/l… | github.com/Levix0501/l… |
预览 | next-admin.fecoder.cn | vue-admin.fecoder.cn |
往期:
实现思路
主题切换需要考虑:
- 组件库的主题切换
- Tailwind CSS / UnoCSS 的主题切换
- 配置的存储
Nextjs
antd
antd
的主题切换可以通过 antd-style
轻松实现
文档:ant-design.github.io/antd-style/…
import { ThemeProvider } from 'antd-style';
export default () => {
return (
<ThemeProvider themeMode={'light'}>
<App />
</ThemeProvider>
);
};
Tailwind
Tailwind
的主题切换可以通过在 html
根元素的 class 上增删 dark 实现
文档:tailwindcss.com/docs/dark-m…
<!-- Dark mode not enabled -->
<html>
<body>
<!-- Will be white -->
<div class="bg-white dark:bg-black">
<!-- ... -->
</div>
</body>
</html>
<!-- Dark mode enabled -->
<html class="dark">
<body>
<!-- Will be black -->
<div class="bg-white dark:bg-black">
<!-- ... -->
</div>
</body>
</html>
存储在 cookie 中
至于配置的存储,我们选择将其保存在 cookie
中,这样可以在服务端渲染时就能知道当前的主题,从而避免客户端生成的 HTML 和服务端生成的 HTML 不一致。
核心代码
完整代码请查看代码仓库
'use client';
import { ReactNode, createContext, useEffect, useState } from 'react';
import { appConfig } from '@/configs/appConfig';
import { useCookieValue } from '../hooks/useCookieValue';
import { useSystemTheme } from '../hooks/useSystemTheme';
import { Settings } from '../types/settings';
import { isDarkTheme } from '../utils/theme';
export type SettingsContextProps = {
settings: Settings;
updateSettings: (settings: Partial<Settings>) => void;
};
type SettingsProviderProps = {
children: ReactNode;
serverSettingsCookie: Settings;
};
export const SettingsContext = createContext<SettingsContextProps | null>(null);
export const SettingsProvider = ({
children,
serverSettingsCookie,
}: SettingsProviderProps) => {
const systemTheme = useSystemTheme();
const [settingsCookie, updateSettingsCookie] = useCookieValue(
appConfig.settingsCookieName,
serverSettingsCookie
);
const [settings, setSettings] = useState<Settings>(settingsCookie);
const updateSettings = (_settings: Partial<Settings>) => {
const newVal = { ...settings, ..._settings };
setSettings(newVal);
updateSettingsCookie(newVal);
};
useEffect(() => {
updateSettings({
systemTheme,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [systemTheme]);
useEffect(() => {
if (
isDarkTheme(settings.themeMode ?? appConfig.defaultThemeMode, systemTheme)
) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [settings.themeMode, systemTheme]);
return (
<SettingsContext.Provider value={{ settings: settings, updateSettings }}>
{children}
</SettingsContext.Provider>
);
};
Vite-Vue
Element-plus
element-plus
的暗色模式只需在 html 上添加一个名为 dark
的类,同时引入 css
文件
文档:element-plus.org/zh-CN/guide…
<html class="dark">
<head></head>
<body></body>
</html>
// main.ts
import 'element-plus/theme-chalk/dark/css-vars.css'
UnoCSS
unocss
的暗色模式也可以通过在 html 上添加一个名为 dark
的类来实现
于是,我们通过在 html 上增删 dark 类,可以同时切换element-plus
和 unocss
的主题
而 vueuse
提供的 useDark
可以帮我们轻松实现在 html 上 增删 dark 类
存储在 LocalStorage 中
对于 CSR
来说,没有特别的需求,所以我们将配置存储在 LocalStorage
中
核心代码
完整代码请查看代码仓库
import { appConfig } from '@/configs/appConfig'
import type { Settings } from '../types/settings'
import { isDarkTheme } from '../utils/theme'
export const useSettings = defineStore('settings', () => {
const settings = useLocalStorage<Settings>(appConfig.settingsStorageName, {
themeMode: appConfig.defaultThemeMode,
systemTheme: appConfig.defaultSystemTheme
})
const isSystemThemeDark = usePreferredDark()
const isDark = useDark()
watch(
settings,
({ themeMode, systemTheme }) => {
isDark.value = isDarkTheme(themeMode, systemTheme)
},
{
immediate: true,
deep: true
}
)
watch(
isSystemThemeDark,
(val) => {
settings.value.systemTheme = val ? 'dark' : 'light'
},
{
immediate: true
}
)
const updateSettings = (_settings: Partial<Settings>) => {
Object.assign(settings.value, _settings)
}
return { settings, updateSettings }
})
结语
至此,我们实现了明暗主题切换
接下去,我们会实现基本色的动态切换
React | Vue | |
---|---|---|
仓库 | github.com/Levix0501/l… | github.com/Levix0501/l… |
预览 | next-admin.fecoder.cn | vue-admin.fecoder.cn |