你有没有留意过这样的应用:晚上打开变成深色背景,白天又变回浅色;或者电商大促期间,整个界面换上节日氛围的配色。
这种“换皮肤”的功能,在小程序里如何实现?如果为每种皮肤单独做一套页面,代码量翻倍,维护成本高得吓人。今天聊聊一套代码支持多种皮肤的技术方案。
第一类是跟随系统的深色模式。用户手机开启深色模式后,小程序自动适配。这是操作系统级别的需求,微信官方提供了标准方案。
第二类是用户主动切换,比如点击“日间/夜间”按钮,或者应用内换主题色(红色版、蓝色版、节日版)。这类需要开发者自己实现。
两者的实现思路不同,但核心都是同一个问题:如何让界面颜色不写死。
微信小程序从基础库2.11.0开始支持深色模式。适配方式有两种。
第一种是在页面JSON文件中配置。在app.json或页面的.json文件里,可以声明深色模式下的样式文件:
{ "darkmode": true, "themeLocation": "theme.json", "dark": { "backgroundTextColor": "#ffffff", "navigationBarBackgroundColor": "#000000" } }
第二种更常用:在CSS中使用媒体查询。在wxss文件里,根据系统主题切换颜色:
/* 默认浅色 */ page { background-color: #f5f5f5; color: #333333; } /* 深色模式覆盖 */ @media (prefers-color-scheme: dark) { page { background-color: #1a1a1a; color: #e0e0e0; } }
这种方式的好处是系统自动判断,用户不需要手动操作。缺点是只有“深/浅”两档,无法支持自定义多色系。
如果你需要支持多个预设主题,或者让用户自定义主色调,CSS变量是最干净的做法。
步骤如下:
第一步,在app.wxss中定义全局变量:
page { --primary-color: #07c160; --bg-color: #ffffff; --text-color: #333333; --border-color: #e5e5e5; } page.theme-dark { --primary-color: #07c160; --bg-color: #1a1a1a; --text-color: #e5e5e5; --border-color: #2c2c2c; } page.theme-red { --primary-color: #e64340; --bg-color: #ffffff; --text-color: #333333; --border-color: #e5e5e5; }
第二步,在组件的wxss中使用这些变量:
.button { background-color: var(--primary-color); color: var(--text-color); } .container { background-color: var(--bg-color); border-color: var(--border-color); }
第三步,在JS中切换主题类名:
// 切换到深色主题 function setDarkTheme() { const page = this page.setData({ themeClass: 'theme-dark' }) wx.setStorageSync('userTheme', 'dark') } // 页面模板中动态绑定 // <view class="{{themeClass}}"> 页面内容 </view>
用户切换后,整个页面的所有使用了CSS变量的地方会自动更新,不需要重新渲染数据。
如果主题之间差异很大——不仅是颜色,还包括布局、字体、组件形状——CSS变量就不够用了。这时可以考虑多份样式文件。
在项目目录下创建多个样式文件:
styles/ base.wxss (公共样式) light.wxss (浅色主题) dark.wxss (深色主题) festival.wxss (节日主题)
在app.wxss中只引入base.wxss。然后在App的onLaunch中,根据存储的主题名称,动态加载对应的样式文件:
// 在app.js中 onLaunch() { const theme = wx.getStorageSync('appTheme') || 'light' this.loadTheme(theme) }, loadTheme(theme) { const styles = { light: '/styles/light.wxss', dark: '/styles/dark.wxss', festival: '/styles/festival.wxss' } if (styles[theme]) { // 使用wx.loadFontFace类似的思路,但样式文件需要用import方式引入 // 更简单的方法:在app.wxss中使用@import,根据不同条件动态切换 } }
不过这种方式有个局限:微信小程序不支持运行时动态卸载已加载的样式。更常见的做法是放弃这个方案,转而采用CSS变量配合多组class。
如果你是个人项目或小团队项目,优先选择CSS变量方案。它足够灵活,代码干净,性能好,维护成本低。
具体实施建议:
第一,在设计阶段就定义好颜色变量。至少包括:主色调、背景色、文字色(分主次)、边框色、警示色。变量名用语义命名而非具体色值,比如--color-brand而不是--color-green。
第二,把主题切换的入口放在“我的”页面或个人设置中。同时记住用户的选择,存储在本地。不要每次打开都重置。
第三,对图片资源也要处理。深色模式下,原本白色的图标可能看不清。解决方案有两种:使用SVG图标并通过CSS控制fill颜色;或者准备两套图片,根据主题切换加载不同的URL。
第四,避免在wxss中直接写死色值。养成习惯:所有颜色都用变量。后期如果需要新增主题,只需要新增一组变量定义,不需要改任何组件样式。
切换主题时,如果页面正在滚动或处于输入状态,用户不希望界面被重置。在切换主题时,应该只改变样式类,不要调用setData去重置数据,更不要重新加载页面。
正确做法是:在全局数据中存储当前主题名称,所有页面通过watch或observer监听这个值的变化,触发本页面的样式更新。这样切换主题时,用户已经输入的内容不会丢失。
一套代码支持多套皮肤,不是什么高深的技术。它的核心是:把颜色和样式参数从组件中抽离出来,变成可配置的变量。无论是系统深色模式、用户自定义主题,还是节日氛围换肤,本质上都是在改变这些变量的值。
下次你再看到一个小程序可以一键换颜色,你就知道它背后大概是怎么实现的了。