如何解决 uni-icons 样式无法覆盖?深入理解 deep() 在 uni-app 中的作用
- 前端
- 3天前
- 16热度
- 0评论
在使用 uni-app 开发项目时,可能会遇到这样一个问题:
在子组件中通过 CSS 设置 uni-icons 图标颜色,却完全不生效。
无论怎么写:
.uni-icons {
color: red;
}
图标就是纹丝不动。
先说结论
uni-icons 样式无法被覆盖,本质上是:
- scoped 样式隔离
- 小程序端重复嵌套结构
- 组件内部样式优先级太高
使用 :deep() + !important 可以一次性解决所有问题,在 H5 和小程序端都能稳定覆盖样式。
:deep() {
.uni-icons {
color: $brand-theme-color !important;
}
}
真正做到跨端统一图标颜色,让项目风格保持一致。
解析问题原因
uni-icons 无法被子组件样式覆盖,通常来自三个方面:
1. <style scoped> 的样式隔离机制
scoped 会为当前组件的 DOM 自动添加一个独立的属性选择器,例如:
<view data-v-123456></view>
这会导致外部书写的样式无法穿透到子组件内部,也无法选中 uni-icons 的真实 DOM 节点。
2. 小程序端 DOM 结构会重复嵌套 uni-icons
在微信小程序、支付宝小程序等平台,uni-icons 会被编译成类似结构:
<uni-icons>
<i class="uni-icons"></i>
</uni-icons>
![]()
结构比 H5 更深、更复杂,导致外部选择器无法命中内部的 .uni-icons。
(补充) scoped 导致匹配不到的真正问题
除了结构嵌套本身更深之外,小程序端还有一个更隐蔽的问题——
scoped 会给父组件加上特殊属性选择器,但不会加到子组件内部的 .uni-icons 上,因此选择器无法匹配到真实 DOM。
例如父组件编译后选择器会变成:
.uni-layout[data-v-123abc] .uni-icons[data-v-123abc] { ... }
但小程序里最终的节点是:
<i class="uni-icons uniu-sound-filled"></i>
注意:这个 <i> 元素没有 data-v-123abc 属性。
也就是说:
- 选择器期望匹配的是:
.uni-icons[data-v-123abc] - 实际 DOM 只有:
.uni-icons
两者不一致,导致选择器始终匹配不到内部的 <i.uni-icons>,样式自然不会生效。
而 :deep() 的作用就在于:
让 .uni-icons 这一部分不再附带 scoped 生成的作用域标记,从而可以直接命中子组件内部真实的 .uni-icons 元素,使样式得以生效。
3. 组件本身的默认样式优先级较高
uni-icons 是封装组件,内部已经定义了 color 样式。
普通 CSS 权重往往不够覆盖,于是出现“不生效”的假象。
解决方案:使用 :deep() 深度选择器穿透作用域
uni-app 中推荐的方式是使用 :deep(),它可以突破 scoped 的样式限制,直接作用于子组件内部。
示例:
:deep() {
.uni-icons {
color: $brand-theme-color !important;
}
}
代码解析
① :deep() —— 深度选择器
Vue3 官方推荐的作用域穿透方式,它能够:
- 穿透
<style scoped>的边界 - 选中子组件内部的元素
- 小程序和 H5 均兼容有效
尤其是小程序端因结构嵌套多层,:deep()能确保样式真正落到内部的.uni-icons上。
② .uni-icons —— 组件生成的真实类名
uni-icons 最终渲染出的 DOM 会包含 .uni-icons 类名,只有选中它才能改变图标颜色。
③ $brand-theme-color —— 自定义主题变量
来自全局 scss 文件的变量,用于统一项目主题色。
例如:
$brand-theme-color: #28B389;
这样可以让所有图标颜色保持风格一致。
④ !important —— 提升样式优先级
由于封装组件内部有默认样式,使用 !important 可以确保你的样式最终覆盖默认设置。
额外建议:主题色统一管理
在 uni.scss 中定义全局主题变量:
$brand-theme-color: #28B389;
组件中直接使用即可:
color: $brand-theme-color;
既统一风格,也便于后期维护。