UI 工程化实战:基于路由的自动主题切换体系
在多租户 SaaS 系统中,不同业务模块往往需要展现截然不同的视觉风格。例如,"电商主页"通常采用蓝色调的商务风格,而"扫码点餐"模块则偏向红色的营销风格。如何让用户在不同模块间跳转时,系统能自动、无感地切换主题,是提升用户体验的关键。
本文将深入解析本项目基于 CSS 变量 + 路由监听 + Pinia Store 构建的自动主题切换体系。
一、 核心痛点
- 手动切换繁琐 :如果依赖开发者在每个页面
onLoad时手动调用setTheme('red'),不仅代码冗余,还极易遗漏。 - 样式难以复用 :硬编码的颜色值(如
#ff4b43)散落在各个组件中,修改主题色需要全局搜索替换。 - 多租户适配难:不同租户可能对同一模块有不同的颜色偏好,静态配置无法满足需求。
二、 架构设计
我们设计了一套三层架构来解决上述问题:
- 定义层 (Theme Definition) :集中定义多套主题的颜色变量(JS)和样式变量(CSS Vars)。 亮点 :除了本地预设,支持运行时通过接口下发变量进行动态覆盖,从而实现"无限自由换肤"。
- 配置层 (Theme Config):建立"路由路径 -> 主题名称"的映射规则。
- 执行层 (Runtime Switcher):监听路由变化,自动匹配并应用主题。
1. 定义层:JS 与 CSS 变量的双重驱动
在 src/store/useStyle/theme/ 目录下,我们定义了 light(默认蓝)、red(营销红)、dark(暗黑)等多套主题。
以 light.ts 为例:
typescript
// CSS 变量:用于组件的样式绑定
export const cssVars = {
'--u-background': '#ffffff',
'--u-text': '#333333'
};
// JS 变量:用于 canvas 绘图或 JS 逻辑
export const colorVars = {
primary: '#2B67F0', // 主色调:蓝
warning: '#FF7D00'
// ...
};
2. 配置层:路由映射规则
在 src/utils/env.ts 中,我们通过 defaultTheme 字段配置了主题的适用范围:
typescript
export const getConfig = () => {
return {
defaultTheme: {
light: '*', // 默认使用 light 主题
dark: [],
red: ['subPackageScanQrOrder'] // 进入"扫码点餐"分包时,强制切换为 red 主题
},
defaultThemeColor: 'light'
};
};
3. 执行层:智能路由监听
在根组件 App.ku.vue 中,我们利用 useStyles Store 提供的能力,在应用启动或路由变化时触发检查。
typescript
<script setup lang="ts">
import { useStyles } from './store/useStyle';
import { parseRouteParams } from './utils/tools';
// 获取当前路径(包含分包路径)
const { hashPath } = parseRouteParams();
if (hashPath) {
// 触发自动主题切换
useStyles().switchThemeByRouter(hashPath);
}
</script>
三、 核心代码解析
src/store/useStyle/index.ts 是整个机制的大脑。
1. 路由匹配逻辑
typescript
const switchThemeByRouter = (routePath: string) => {
const config = getConfig();
// 1. 提取路径前缀(分包名)
let pathPrefix = routePath.split('/').filter(Boolean)[0];
// 2. 遍历配置,寻找匹配的主题
let key = Object.keys(config.defaultTheme).find(key => {
// 检查当前路径是否在某个主题的配置列表中
return config.defaultTheme[key as ThemeColor]?.includes(pathPrefix);
});
// 3. 兜底策略:未匹配则使用默认主题
if (!key) {
switchTheme(config.defaultThemeColor);
return;
}
// 4. 执行切换
switchTheme(key as ThemeColor);
};
2. 样式注入逻辑
switchTheme 方法负责将选定主题的变量注入到 uview-pro 的主题系统中,使其全局生效。
typescript
const switchTheme = (theme: ThemeColor) => {
if (currentThemeColor.value === theme) return; // 避免重复切换
const themeVars = getTheme(theme);
// 更新 Pinia 状态
currentThemeColor.value = theme;
// 调用 UI 库的 setTheme 方法,底层会将 CSS 变量挂载到 :root 上
changeTheme({
selfColorsVars: themeVars.colorVars,
selfCssVars: themeVars.cssVars
});
};
四、 方案优势
- 零侵入性:业务页面无需编写任何主题切换代码,只需关注业务逻辑。
- 细粒度控制:支持按分包、按页面甚至按租户配置不同的主题规则。
- 动态性:除了内置主题,该架构还支持从服务端下发 JSON 配置,实现"千人千面"的动态换肤。
- 一致性:JS 变量与 CSS 变量同步更新,确保 Canvas 图表与 DOM 元素的颜色始终保持一致。
通过这套 UI 工程化体系,我们成功实现了复杂业务场景下的自动化视觉管理,显著提升了开发效率与用户体验。