前言
换肤是对应用的主题进行切换。需要在不修改代码和结构的前提下,通过动态切换一套样式规则,来改变应用的整体视觉外观,从而为用户提供不同的视觉体验。
而在uniapp中实现换肤,就需要考虑不同平台的实现,本文将介绍一种比较通用的最佳解决方案。
主要亮点有:
- 主题色后台进行不同平台配置,通过接口获取
- 无需使用类名切换,直接使用变量
- 可以利用主题色,衍生出颜色色阶,无需额外定义
在线预览:

接下来话不多说,开始实践部分。
1. 整体实现方式
整体流程梳理:
- 后台定义主题色等颜色
- 接口请求主题色,转换成CSS变量。
注意: 主题色需要转换成rgb,用于衍生出其他颜色色阶
, 如--mainColor:#FF4757, --mainRgbColor:255,71,87
- 页面根元素绑定,
style="--mainColor:#FF4757"
- 页面使用,
color: var(--mainColor);background: rgba(--mainRgbColor, 0.5)

说明:通用标签的背景色是根据主题色衍生出来的,这里就需要转换成rgb的形式进行使用。
2. 案例说明
接下来我们使用uniapp+vue3+pinia
进行案例演示,包括编译后的app、h5、小程序
进行实践。
我们整体代码实现思路是:通过pinia进行整体的设置和存储,接口获取时,我们需要额外计算出主题色的rgb
。
2.1 pinia设置和存储
我们使用pinia进行统一管理我们的换肤颜色。
ini
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useThemeStore = defineStore(
'theme',
() => {
const themeInfo = ref(`--bg:#F8F8F8;--mainColor:#FF4757;--mainRgbColor:255,71,87;--subColor:#FFECED;--priceColor:#FF3838;`);
const setTheme = (val: string) => {
themeInfo.value = val;
};
const resetTheme = () => {
themeInfo.value = '';
};
return {
themeInfo,
setTheme,
resetTheme,
};
},
{
persist: true
}
);
2.2 接口请求
我们需要请求自己平台的主题色,进行设置存储。需要将主题色进行转换成rgb形式,方便后续使用。
注意:如果在less中,我们可以使用fade进行rgba的操作,问题是fade函数不支持变量形式,仅仅只能用
fade(#FFFFFF,0.2)
。 如果使用变量,我们只能转换成rgb的字符,利用rgba实现
js
import { useThemeStore } from '@/store';
const hexToRgbStr = (hex: string): string => {
try {
// 扩展 3 位格式到 6 位
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) => {
return r + r + g + g + b + b;
});
// 解析 6 位 HEX 格式
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (!result) return ''
return `${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt(result[3], 16)}`;
} catch (error) {
return ''
}
}
const handleChange = () => {
const mainColor = '#FF4757'
const mainRgbColor = hexToRgbStr(mainColor)
const colorStr = `--mainColor:${mainColor};--mainRgbColor:${mainRgbColor};--subColor:#FFECED;--priceColor:#FF3838;`
useThemeStore().setTheme(colorStr)
}
2.3 页面使用
页面使用需要在页面的根元素绑定style
,这样就可以在css中使用颜色变量了。
js
<div :style="useThemeStore().themeInfo"></div>
css
.main {
color: var(--mainColor);
background: rgba(var(--mainRgbColor),0.3);
}
2.4 一点开发优化
我们在使用css的时候,会一直使用var
,我们希望用更简单的方式进行书写。例如:
scss
.main {
color: $color-theme;
background: rgba($color-rgb-theme,0.3);
}
这种写法让我们的开发更加舒服,那么怎么去实现这样呢?也很简单,我们只需要定义一个通用的skin.scss
,将这些变量定义进去,全局引入即可使用。
下面介绍两个引入方案:
2.4.1 使用vite全局引入
方案一:
- 定义skin.scss,定义变量
- 如果直接引入到页面会报错,我们直接在vite.config.ts中声明引入
- 页面直接使用
如果我们不想使用var()
这种形式,需要在vite.config.ts中声明提前加载的css:
ts
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/skin.scss";` // 全局注入
}
}
}
skin.scss:
scss
$color-bg: var(--bg);
$color-theme: var(--mainColor);
$color-rgb-theme: var(--mainRgbColor);
$color-theme-d: var(--subColor);
$color-price: var(--priceColor);
这样就可以使用$形式,可以去除var这种写法。
css
.main {
color: $color-theme;
}
2.4.2 使用uni.scss引入
方案二:
我们需要使用到uni.scss
,它是一个特殊文件,在代码中无需 import 这个文件即可在scss代码中使用这里的样式变量。uni-app的编译器在webpack配置中特殊处理了这个uni.scss,使得每个scss文件都被注入这个uni.scss,达到全局可用的效果。
我们在此只需要将skin.scss
定义的css变量移动到uni.scss
中定义即可,也无需在vite中声明。
scss
$color-bg: var(--bg);
$color-theme: var(--mainColor);
$color-rgb-theme: var(--mainRgbColor);
$color-theme-d: var(--subColor);
$color-price: var(--priceColor);
3. 效果展示


4. 总结
最后总结一下,主要就是利用CSS变量和RGB转换,实现换肤效果,并且在书写中,优化了一点开发体验。
如有错误,请指正O^O!