[前端][uni-app]子组件上添加的样式类没有起作用
scoped 样式隔离原理
在 vue 文件中的 style 标签上,有一个特殊的属性:scoped。 当一个 style 标签拥有 scoped 属性时,它的 CSS 样式就只能作用于当前的组件,通过该属性,可以使得组件之间的样式不互相污染。(无法隔离标签选择器的样式)
为了方便解释,以下说明全部基于以下示例代码:
<template>
<div class="em-demo" @click="onClick">
<!--子组件em-loading最外层的节点类名为 em-loading-->
<em-loading class="childClass"></em-loading>
</div>
</template>
<style lang="scss" scoped>
@import './css/em-demo.scss';
</style>
// em-demo.scss 内容
.em-demo{
font-size: $uni-font-size-base;
color: $uni-text-color;
.childClass{
background-color: red;
// 子组件内部节点类名
::v-deep .em-loading-circular{
background-color: blue;
}
}
}
原理
1、为组件实例生成一个唯一标识,给组件中的每个标签对应的 dom 元素添加一个标签属性,data-v-xxxx。 2、给<style scoped>中的每个选择器的最后一个选择器添加一个属性选择器,如:原选择器为 .em-demo ,则编译后选择器为 .em-demo[data-v-xxxx]。
样式穿透
1、方法
样式穿透的写法有三种:>>>、/deep/、::v-deep
(1)css 原生样式,可以直接使用 >>> 穿透修改
(2)若项目中用到了预处理器 scss 、sass、less, 操作符 >>> 可能会因为无法编译而报错 ,此时可以使用 /deep/或者 ::deep。
2、原理:
在 deep 之后的选择器最后就不会加上标识。
各平台差异
H5 平台
(1)为组件实例生成一个唯一标识,给组件中的每个标签对应的 dom 元素添加一个标签属性,data-v-xxxx。
(2)直接加在子组件上的 childClass,还是属于父组件的,编译后为 .em-demo .childClass[data-v-父组件标识]。
(3)childClass 直接添加在子组件的最外层节点上,与 em-loading 这个类名在同节点,所以可以影响到子组件最外层节点的样式。
(4)子组件内部的节点类名 em-loading-circular,父组件无法影响,需要进行样式穿透。
微信小程序平台
(1)uniapp 编译到小程序平台后,会在原组件上添加 data-v-xxxx 的类名,而不是属性。
(2)直接加在子组件上的 childClass,还是属于父组件的,编译后选择器为 .em-demo .childClass.data-v-父组件标识。
(3)小程序平台的子组件外层会新增一层节点,childClass 添加在新增的节点上,与 em-loading 这个类名不在同节点。只能影响新增的这一层节点的样式。
(4)子组件的所有节点类名 em-loading em-loading-circular,父组件都无法影响,使用样式穿透依然不行。这是因为小程序也有自己的一套样式隔离方案,具体请看微信小程序自定义组件样式。
所以,如果要父组件的样式可以对子组件产生影响,还要在父组件中进行配置:
export default {
options: {
styleIsolation: 'shared'
}
};
这样,父组件使用样式穿透,就可以对子组件样式产生影响了。
3、在支付宝小程序平台: uniapp 编译到小程序平台后,会在原组件上添加 data-v-xxxx 的类名,而不是属性。 支付宝小程序表现与微信小程序的(1)(2)(3)三点相同,第(4)点则与 H5 环境下相同,只需要进行样式穿透,就可以对子组件样式产生影响。
解决方案
需要修改子组件样式时,为了避免各端表现差异,建议通过 props 给子组件传递类名,开启小程序的样式隔离配置,然后通过样式穿透修改子组件的样式。