先说个前序,有多少人把scoped写成scope的…
原理:
在 vue 去编译组件的时候,如果当前 style 标签上有 scoped 属性,那么就会为当前组件所有标签(若有子组件,则会在子组件最外层标签)添加上一个 data-v-hash 的属性,而当前样式表的所有尾部选择器后面也会加上该属性,那么就使得当前组件内的样式只作用域当前组件内的元素。
通过 PostCSS 实现以下转换
<template>
<div class="test">hello</div>
</template>
<style scoped>
.example {
color: red;
}
</style>
转换后
<template>
<div class="test" data-v-efdef544>hello</div>
</template>
<style>
.example[data-v-efdef544] {
color: red;
}
</style>
可能出现的情况:
1.当父组件无scoped属性,子组件带有scoped,子组件所有标签会套上子组件的data-v-hash属性,
2.当父组件有scoped属性,子组件无scoped.父组件所有标签和子组件的最外层标签有父组件的data-v-hash属性.
3.父子组件都有scoped,父组件的所有标签有父组件的data-v-hash属性,子组件的所有标签有子组件的data-v-hash属性,这时子组件的最外层标签就会同时套上父组件和子组件的data-v-hash属性
导致的问题
这里以情况3设个例子:
父组件
<template>
<div class="father">
<-- 子组件 -->
<son><son>
<-- 子组件 -->
</div>
</template>
<style scoped>
.son .son-box {
color: red;
}
</style>
子组件
<template>
<div class="son">
<div class="son-box">
abc
</div>
</div>
</template>
<style scoped>
.son-box {
color: yellow;
}
</style>
转换后
<template>
<div class="father" data-v-acc63fea>
<-- 子组件 -->
<div class="son" data-v-4475b032 data-v-acc63fea>
<div class="son-box" data-v-4475b032>
abc
</div>
</div>
<-- 子组件 -->
</div>
</template>
<style scoped>
<-- 父组件样式,作用不了 -->
.son .son-box[data-v-acc63fea] {
color: red;
}
<-- 子组件样式 -->
.son-box[data-v-4475b032] {
color: yellow;
}
</style>
解决的办法
方法一: 通过外联css(即在外创建.css文件,在组件的<style scoped></style>
中通过@import引入)
原理: 通过@import引入,外联样式表的所有尾部选择器后面不会加上data-v-hash属性
缺点: 会造成有多个css文件,若在子组件中应用时,因为通过@import引入的文件是没有data-v-hash唯一标识的,即这个文件的样式会造成组件间的污染
适用场景: 设置所有组件的全局样式时使用,即应用在app.vue
方法二: 在同一个组件, 同时存在两个<style scoped></style>,<style></style>
原理: 不加scoped的style,所有尾部选择器后面不会加上data-v-hash属性
优点: 方便修改
缺点: 若不是独一无二的class类,不带scoped的可能造成全局污染
使用场景: 该组件中有一个独一无二的class类进行包裹
方法三(推荐): 使用深度选择器(也叫样式穿透)
注意: (less,sass,scss等预编译)使用(/deep/或者::v-deep),非预编译的话是(>>>)
原理: 会将 /deep/或>>> 替换成对应组件的 hash 值
优点: 可以在scoped直接修改子组件样式
缺点: 没有缺点,实在说有缺点的话,就是要写(/deep/或>>>,::v-deep)
使用场景: 所有
注意: 使用/deep/在同等情况下,默认父组件设置的子组件样式权重比子组件内的高,但子组件设置的样式有嵌套父类,则子组件的高,但父组件也可以嵌套同样的父类,这样父组件权重还是比子组件的高
使用深度选择器后,通过 PostCSS 实现以下转换(注意,我没用预编译,所以应该使用>>>)
<template>
<div class="test">hello</div>
</template>
<style scoped>
>>> .example {
color: red;
}
</style>
转换后
[data-v-efdef544] .example {
color: red;
}
</style>
最后,如果本文有哪些不对或者可以改善的,请评论指出,谢谢!