Scoped CSS和CSS Modules都是对vue单文件组件中的style标签的一种修饰,使我们能更好地处理不同情况下的样式使用
Scoped CSS
使用
Scoped CSS的使用只需要在style标签上添加scoped即可
<template>
<div class="example">hello world</div>
</template>
<style scoped>
.example{
color:#f00;
}
</style>
使用scoped的style,编译后其内的选择器会在其后添加自定义属性data-v-[str],str为一串字符串,而使用该选择器的标签也会写上该自定义属性,而且只会修改当前组件中使用该选择器的标签,所以该选择器也就只能作用于当前组件
上面的代码会编译为:
<template>
<div class="example" data-v-f3f3eg9>hello world</div>
</template>
<style scoped>
.example[data-v-f3f3eg9]{
color:#f00;
}
</style>
混用本地和全局样式
我们称使用scoped的style为本地样式,而不使用scoped的style为全局样式,本地样式只能应用于本组件,而全局样式能应用于当前页面的所有组件
因为在vue单文件组件中,可以有多个style,所以我们可以混用本地样式和全局样式
<style>
/*全局样式*/
</style>
<style scoped>
/*本地样式*/
</style>
深度作用选择器
如果我们要让scoped组件中的部分选择器能应用到子组件,我们可以通过写一个新的全局样式来将其深入到子组件中,此外,也可以通过深度作用选择器>>>来作用到子组件中
<style scoped>
.a >>> .b { /* ... */ }
</style>
上面的代码会编译成
<style scoped>
.a[data-v-f3f3eg9] .b { /* ... */ }
</style>
可以看到,自定义属性被定义在了>>>的位置,所以.b选择器的样式可以作用到子组件中
此外,像Sass,less等一些预处理器无法正确处理深度作用选择器>>>,可以使用/deep/或者::v-deep来代替。
<style lang="less" scoped>
.a ::v-deep .b{ /* ... */ }
</style>
特殊情况
子组件的根元素
子组件的根元素同时处在当前组件及其子组件中,所以scoped中的样式会对当前子组件的根元素起作用,而子组件中的样式也会对其起作用
<!-- 当前组件 -->
<template>
<div class="example">
<child></child>
</div>
</template>
<style scoped>
.childExample{
color:#f00;
}
</style>
<script>
import child from './child.vue'
export default {
data(){
return{}
},
components:{
child
}
}
</script>
<!-- 子组件 -->
<template>
<div class="childExample">hello world</div>
</template>
<style scoped>
.childExample{
font-size:20px;
}
</style>
这里的color和font-size样式都会应用在hello world上
动态生成的内容
通过v-html生成的DOM内容不受scoped样式影响,即使是在同一组件内,因为不会在其标签上添加自定义属性,如果要作用在v-html生成的内容上,可以使用深度选择器来应用在其上
<template>
<div id="app">
<span class="example">1</span>
<div v-html="msg"></div>
</div>
</template>
<script>
data(){
return{
msg:"<span class='example'>123</span>"
}
}
</script>
<style scoped>
.example{
color:#f00;
}
</style>
上面的代码编译后会变为
<div data-v-7ba5bd90="" id="app">
<span data-v-7ba5bd90="" class="example">1</span>
<div data-v-7ba5bd90="">
<span class="example">123</span>
</div>
</div>
可以看到v-html生成的span标签上没有自定义属性,所以scoped上的样式不会对其起作用。
CSS Modules
使用
CSS Modules是一个用于模块化和组合CSS的系统,在vue-cli搭建的项目中可以直接使用,在webpack中可通过给css-loader传入modules:true来启用
// webpack.config.js
{
module: {
rules: [
// ... 其它规则省略
{
test: /\.css$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
// 开启 CSS Modules
modules: true,
// 自定义生成的类名
localIdentName: '[local]_[hash:base64:8]'
}
}
]
}
]
}
}
在style标签上加上module,就使用了CSS Module了
<style module>
.example{
color:#f00;
}
</style>
上面的代码会编译为
.App_example_1GtWg {
color: #f00;
}
CSS Module应用的style标签上的样式,会被写在名为$style的计算属性上,在template中可以通过:class来应用该计算属性
<template>
<div id="app">
<span :class="$style.example">1</span>
</div>
</template>
上面的代码会编译为
<div id="app">
<span class="App_example_1GtWg">1</span>
</div>
JavaScript中访问样式
因为CSS Modules中的样式写在计算属性$style中,所以可以在JavaScript中访问到
<script>
export default {
data(){
return{}
},
created () {
console.log(this.$style.example)
}
}
</script>
这样会打印出编译后的样式名
子组件中使用
直接写上class名
直接打开页面后使用f12查看,然后直接写上编译后的类名
<!-- 子组件中 -->
<span class="App_example_1GtWg">22</span>
使用props传递
在父组件中,将$style传递到子组件中,子组件就能直接调用里面的字符串
<!-- 当前组件 -->
<template>
<div id="app">
<Child :parentStyle="$style"></Child>
<span :class="$style.example">1</span>
</div>
</template>
<script>
import Count from './count.vue'
export default {
data(){
return{}
},
components: {
Child
}
}
</script>
<style module>
.example{
color:#f00;
}
</style>
<!-- 子组件 -->
<template>
<div>
<span :class="parentStyle.example">22</span>
</div>
</template>
<script>
export default {
props:["parentStyle"]
}
</script>
这样子子组件中也能使用父组件中的CSS Modules的样式
自定义名称注入
CSS Modules允许我们自定义生成的计算属性的名称,直接在style标签上写即可
<template>
<div id="app">
<span :class="a.example">1</span>
</div>
</template>
<script>
export default {
data(){
return{
}
}
}
</script>
<style module="a">
.example{
color:#f00;
}
</style>
注意点
自定义名称的使用
在同一组件内,我们可以为不同的style标签设置不同的自定义CSS Modules名称
<style module="a">
.example1{
color:#f00;
}
</style>
<style module="b">
.example2{
color:#0f0;
}
</style>
但是要注意的是,在同一组件内,不同的module名称对同一选择器名编译生成的名字相同
如下面的代码
<style module="a">
.example{
color:#f00;
}
</style>
<style module="b">
.example{
color:#0f0;
}
</style>
编译后查看控制台
发现使用了相同的选择器名,所以即使在使用不同Modules名称的style标签中,样式对于当前组件来说是一样的,后写的样式会覆盖先写的样式,当然也正因这样我们可以通过调用不同的module来达到切换样式的效果。
Scoped CSS和CSS Modules的比较
相同点
- 两者都可以达到将样式只应用在当前组件,而不会影响到其他组件的效果,Scoped CSS是通过使用自定义属性,而CSS Modules是通过修改选择器名
- 两者都有办法将样式传递到子组件中,Scoped CSS是通过使用深度选择器,而CSS Modules是通过传递选择器名
- 在同一组件中使用多个style标签时,后面使用的style标签的样式会覆盖前面style标签的样式,即使CSS Modules中设置了不同的自定义名
- 两者在使用vue cli搭建的项目中都是开箱即可用
不同点
- 如上面所说的,达到只应用于当前组件和传递到子组件的方式不同
- CSS Modules可以通过JavaScript得到修改后的类名,而Scoped CSS不行
- CSS Modules在自己配置时需要将css-loader中的modules设置为true