Skyroc Admin React
主题与资源

主题运行时与缓存

理解 setupTheme、BUILD_TIME、storage、overrides 与 ThemeEffect 在后台主题初始化和持久化中的职责

这一页解释主题配置从“默认值”变成“用户当前看到的主题”的完整运行链路。它主要回答三个问题:

  1. 主题为什么要在 bootstrap.tsx 里先初始化。
  2. 生产环境为什么会读取旧缓存,以及什么时候应用新版本覆盖。
  3. ThemeEffect 为什么负责写 DOM class、localStorage、滤镜和水印,而不是页面组件自己处理。

当前 React 版主题链路是:

bootstrap.tsx
  -> setupTheme({ buildTime, storage, overrides? })
  -> App render
  -> AppAntdProvider
  -> @skyroc/web-admin-theme/AntdProvider
  -> GlobalEffect
  -> ThemeEffect

主题状态的运行时数据源是 themeSettingsAtom。localStorage 只是持久化手段,不是页面读取主题的入口。

实现位置

文件职责
apps/admin-example/src/bootstrap.tsx在 React 渲染前调用 setupTheme(),并把应用级 localStg 交给主题包。
apps/admin-example/src/utils/storage.ts创建应用级 storage,默认前缀为 SR_,可由 VITE_STORAGE_PREFIX 覆盖。
packages/web/admin-theme/src/setup.ts读取默认主题、生产缓存和版本覆盖,初始化 themeSettingsAtom.init
packages/web/admin-theme/src/hooks/use-theme.ts暴露 useTheme() / useSettingsTheme(),统一读写主题状态。
packages/web/admin-theme/src/components/ThemeEffect.tsx把主题状态同步到 DOM、localStorage、全局滤镜和水印定时器。
apps/admin-example/src/features/effects/GlobalEffect.tsx挂载 ThemeEffectLangEffect

setupTheme 的职责

setupTheme() 必须在任何组件读取主题 atom 之前执行。当前 apps/admin-example/src/bootstrap.tsx 的主题初始化是:

setupTheme({
  buildTime: BUILD_TIME,
  storage: localStg
});

这里有两个关键参数:

参数作用
buildTime用于生产环境判断当前构建版本是否已经应用过主题覆盖。
storage指定主题包读写缓存时使用的 storage adapter。传入 localStg 后,主题缓存会跟随 VITE_STORAGE_PREFIX

如果不传 storagestoragePrefix,主题包会用自己的默认前缀 SR_ 创建 storage。默认情况下这和应用前缀一样,但当应用配置了 VITE_STORAGE_PREFIX 后,主题包不会自动知道这个变化。显式传 localStg 可以避免 token、语言、tabs 使用一个前缀,主题仍写到另一个前缀。

初始化规则

setupTheme() 的内部规则按环境分成两段:

环境行为
开发环境直接用 defaultThemeSettings 初始化主题 atom,不读取缓存。
生产环境读取 themeSettings 缓存,与默认主题合并,再根据 overrideThemeFlagbuildTime 判断是否合并 overrides

生产环境会先读缓存:

const cachedSettings = themeStorage.get('themeSettings');
let settings = mergeThemeSettings(cachedSettings, defaultThemeSettings);

这表示用户在主题抽屉里调整过的配置会优先保留。默认主题新增字段时,mergeThemeSettings() 会把缺失字段补回去,避免旧缓存结构导致运行时字段不存在。

版本覆盖

overrides 是“新版本发布后需要覆盖用户缓存的字段”,不是普通默认值。

setupTheme({
  buildTime: BUILD_TIME,
  storage: localStg,
  overrides: {
    themeColor: '#2563EB',
    themeScheme: 'light'
  }
});

主题包会用 overrideThemeFlag 记录当前构建时间。如果当前缓存中的 flag 不等于 BUILD_TIME,才把 overrides 合并到缓存主题上,并写入新的 flag。

适合放进 overrides 的情况:

场景是否适合
产品新版要求统一替换默认主色适合。
新增 watermark 字段,需要给生产用户补默认结构适合。
只想让开发环境临时看一个颜色不适合,直接用主题抽屉或改共享默认值。
每次启动都想强制重置用户主题不适合,overrides 不是重置机制。

如果只是共享默认值本身要调整,应改 packages/web/admin-theme/src/config/default.ts。如果只是某个应用有自己的默认偏好,应在应用入口传 overrides,不要把应用专属配置写进共享主题包。

主题缓存 key

主题包当前会读写这些 key:

key写入时机
themeSettings生产环境页面关闭或刷新前,缓存完整主题配置。
darkMode暗色模式派生值变化时写入。
themeColor主题主色变化时写入。
overrideThemeFlag生产环境应用过当前构建版本的 overrides 后写入。

themeSettings 是完整主题配置,供下一次生产环境初始化恢复用户配置。darkModethemeColor 是轻量快照,主要给首屏 loading 这类早于完整 React 主题链路的界面使用。

ThemeEffect 的职责

ThemeEffect 不渲染 UI,它只做主题副作用:

副作用目的
toggleCssDarkMode(darkMode)在 html 上同步暗色 class,让 UnoCSS、全局样式和暗色工具类生效。
set('darkMode', darkMode)缓存当前暗色派生值,供首屏 loading 读取。
set('themeColor', themeColors.primary)缓存当前主色,供首屏 loading 读取。
beforeunload 缓存 themeSettings生产环境刷新或关闭前保留完整用户主题设置。
toggleAuxiliaryColorModes()同步灰阶和色弱模式。
updateWatermarkTimer()仅在水印开启且显示时间时运行水印时间更新。

这些操作都属于 React 外部系统同步,所以集中在 ThemeEffect。页面组件不应该自己写 effect 去改 document.documentElement、localStorage 或全局滤镜。

首屏 loading 的主题快照

apps/admin-example/src/pages/loading.tsx 会读取:

const { defaultDarkMode, defaultThemeColor } = globalConfig;

globalConfig.defaultThemeColorglobalConfig.defaultDarkMode 又会从 storage 快照读取 themeColordarkMode,没有缓存时回退到 defaultThemeSettings

这条链路的重点是:loading 页不依赖完整的 useTheme(),因为它可能在路由 pending 阶段显示。它只读取主题快照,让首屏颜色和暗色模式尽量贴近用户上一次选择。

排查顺序

  1. 默认主题不生效,先确认 setupTheme() 是否在 createRoot().render() 之前执行。
  2. 修改 VITE_STORAGE_PREFIX 后主题没跟着变,检查 setupTheme() 是否传入 storage: localStgstoragePrefix
  3. 生产环境旧用户看不到新版主题,检查 BUILD_TIME 是否变化,以及 overrideThemeFlag 是否已经等于当前构建时间。
  4. 主题抽屉修改后刷新丢失,确认 ThemeEffect 是否挂载在 GlobalEffect 中。
  5. 暗色模式 UI 部分生效、部分不生效,检查 html dark class、Ant Design Provider 和页面自己的样式覆盖。

常见误区

误区正确做法
overrides 当成每次启动的默认值overrides 只在生产环境构建版本变化时覆盖缓存字段。
页面直接读 localStorage 拿主题页面读取 useTheme(),localStorage 只给初始化和持久化使用。
在页面里手动切 html dark class暗色 class 由 ThemeEffect 统一同步。
只传 buildTime 就以为主题缓存跟随应用前缀还要传 storagestoragePrefix
修改共享默认主题来满足单个应用应用专属配置放在应用入口的 overrides 或 storage 初始化策略。

相关链接

主题继续查看
主题系统总览主题系统
Ant Design token 和 UnoCSS 主题变量主题 Token 与 Ant Design
启动顺序启动流程
存储前缀和缓存 key存储与缓存

On this page