# css-scoped
为了防止样式冲突,Vue 提供了给 css 文件加上 scoped 属性的方法:
<template>
<div class="btn"></div>
</template>
<style scoped>
.btn {}
</style>
1
2
3
4
5
6
2
3
4
5
6
# 原理
编译后会给元素标签和 css 都加上 【data-v-hash】,如下:
<body>
<div class="btn" data-v-12345678></div>
</body>
<style>
.btn[data-v-12345678] {}
</style>
1
2
3
4
5
6
2
3
4
5
6
其中 hash 值是由文件路径和文件内容通过 hash-sum 生成:
// vue-loader/lib/index.js
const hash = require('hash-sum')
module.exports = function (source) {
// ...
const shortFilePath = rawShortFilePath.replace(/\\/g, '/') + resourceQuery
// 下面即 hash 值
const id = hash(
isProduction
? (shortFilePath + '\n' + source.replace(/\r\n/g, '\n'))
: shortFilePath
)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 子组件最外层元素
当父子组件同时设置 scoped 属性时,子组件最外层的元素既会被加上当前组件的 hash 值,也会加上父级组件的 hash 值:
// Parent.vue
<script>
export default {
components: {
Child
}
}
</script>
<style scoped>
</style>
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
// Child.vue
<template>
<div>
<div></div>
</div>
</template>
<style scoped>
</style>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
则 Child 组件 dom 结构在被编译之后如下:
<div data-v-ParentHash data-v-ChildHash>
<div data-v-ChildHash></div>
</div>
1
2
3
2
3
所以当子组件最外层元素设置的 class 在父组件中也存在时,则最外层样式会被父组件设置的样式影响。
# 深度作用选择器
当父组件设置 scoped 之后,如果还想要部分 class 可以覆盖子组件的样式就需要设置 '>>>' 属性:
<style scoped>
.parent-self >>> .child-klass {}
</style>
1
2
3
2
3
编译之后会变为:
<style>
.parent-self[hash-v-ParentHash] .child-klass {}
</style>
1
2
3
2
3
但在 sass 等预处理工具中 '>>>',会报错,可以用 '/deep/' 属性,如下:
<style scoped>
.parent-self /deep/ .child-klass {}
</style>
1
2
3
2
3
如果在 vue-cli3 中遇到报错,也可以使用 '::v-deep' 属性:
<style scoped>
.parent-self ::v-deep .child-klass {}
</style>
1
2
3
2
3