小程序不支持document.documentelement.style.setproperty,因无document对象;应改用css变量+class切换方案,预先定义:data-theme样式并动态绑定根节点属性。

小程序端不能用 document.documentElement.style.setProperty
这是最常踩的坑。很多开发者照搬 H5 的写法,在 onLoad 里调用 document.getElementById('app').style.setProperty('--primary', '#ff6b6b'),结果在微信/支付宝/百度小程序里完全不生效——因为小程序运行环境没有 document 对象,也没有全局 html 或 body 元素。uni-app 编译后的小程序代码运行在原生渲染层(如 WebView 或自定义渲染器),document 是被屏蔽的。
实操建议:
- 放弃所有基于
document/window的 CSS 变量注入方式 - 改用
<style></style>标签动态插入字符串样式(仅限 H5)或彻底转向「CSS 变量 + class 切换」方案 - 确认当前平台:用
uni.getSystemInfoSync().platform === 'mp-weixin'做条件判断,避免误执行报错
必须用 :root[data-theme] + CSS 变量 + class 绑定
小程序支持有限的 CSS 变量能力(基础库 ≥ 2.11.0),但只认编译时已存在的变量声明,且不支持运行时通过 JS 修改 :root 的 style 属性。可行路径是:预先在 App.vue 的 <style></style> 中定义多套 :root[data-theme="light"] 和 :root[data-theme="dark"],再通过 uni.$u.theme 控制根节点的 data-theme 值。
示例(App.vue):
:root[data-theme="light"] {
--primary-color: #1890ff;
--bg-color: #ffffff;
}
:root[data-theme="dark"] {
--primary-color: #13c2c2;
--bg-color: #1f1f1f;
}
然后在 onLaunch 中设置:
const theme = uni.getStorageSync('theme') || 'light'
uni.$u.theme = theme
// 注意:必须手动给根节点加 class 或 data-* 属性
const rootEl = uni.createSelectorQuery().in(this).select('body')
// ❌ 小程序无 body;✅ 改用 this.$scope(仅 App.vue 可用)
if (this.$scope && this.$scope.setData) {
this.$scope.setData({ 'data-theme': theme })
}
更稳妥的做法是:在每个页面的 <view class="page" :class="'theme-' + $u.theme"></view> 上绑定主题 class,并让所有样式都基于该 class 层级书写。
主题色无法穿透组件 scoped 样式
用了 scoped 的 <style></style>,:root 定义的变量在子组件里读不到,或者被覆盖。比如 uni-button 内部样式写死 background-color: #007aff,你设了 --primary-color 也没用。
实操建议:
- 所有主题相关颜色,统一走
css var(--primary-color),禁用硬编码色值 - 第三方组件(如
uni-ui)需手动覆盖其 class,例如:.uni-button--primary { background-color: var(--primary-color) !important; } - 避免在
scoped样式里写:root,把它提到App.vue的非scoped<style></style>中 - 图标颜色若用
uni-icon,需配合colorprop 动态传入$u.themeColors.icon,不能依赖 CSS 变量自动继承
主题切换后页面不刷新,旧样式残留
小程序页面生命周期中,onShow 不会重新解析 <style></style>,所以即使你改了 data-theme,已渲染的节点不会自动重绘。尤其在 TabBar 页面间跳转时,视觉状态明显滞后。
实操建议:
- 换肤后主动触发一次
uni.reLaunch({ url: '/pages/index/index' })(慎用,体验差) - 更推荐:监听
uni.$on('themeChange'),在每个页面onShow中强制更新关键元素的class或style - 对
<page></page>级容器加:key="$u.theme",强制 Vue 重建整个页面 DOM(仅限非scoped样式生效的场景) - 持久化必须同步:换肤后立刻
uni.setStorageSync('theme', newTheme),否则冷启动时还是旧主题
小程序端主题同步真正的难点不在“怎么设”,而在“怎么确保每一处都响应”。从导航栏、TabBar、自定义组件、SVG 图标到第三方 UI 库,每层都需要显式适配。没有银弹,只有逐层覆盖和测试。











