样式冲突
Vue多组件开发时,组件中的CSS样式默认是全局使用的。例如在A.vue
这个组件中定义的样式,也会在B.vue
这个组件中生效。
而当项目中使用的组件越来越多时,这种全局通过的CSS样式的方式势必会影响整个项目,导致样式冲突。例如A.vue
和B.vue
都用了.demo
这个样式,那么在引入和使用组件时,会造成样式的冲突。相同选择器的样式,究竟是哪个组件中的样式生效?
Vue在引入组件.vue
时,如果存在相同的样式导致的冲突,后引入的组件会覆盖先引入的组件中相同的样式。例如A.vue
和B.vue
都用了.demo
这个样式:
import A from './components/A.vue'
import B from './components/B.vue'
// B 中的 .demo 会覆盖 A 中的 .demo,并全局生效
Scoped
为了解决这种样式冲突的问题,Vue引入了scoped
(样式范围)。使用scoped
可以让当前组件中的样式,仅对当前组件模板中的元素生效(样式局部生效,防止冲突)。
scoped
的用法是,在.vue
中,直接作用在<style>
标签上:
<style scoped>
/* ... */
</style>
例如:
<template>
<div class="demo">
<h2>用户姓名:{{name}}</h2>
<h2>用户年龄:{{age}}</h2>
<h2>用户性别:{{sex}}</h2>
</div>
</template>
<script>
export default {
name: 'SiteUser',
data() {
return {
name: '张三',
sex: '男',
age: 21,
}
},
}
</script>
<style scoped>
.demo {
background-color: orange;
}
</style>
<template>
<div class="demo">
<h2>网站名称:{{name}}</h2>
<h2>网站地址:{{url}}</h2>
</div>
</template>
<script>
export default {
name: 'MySite',
data() {
return {
name: 'Linner\'s Blog',
url: 'blog.linner.asia',
}
},
};
</script>
<style scoped>
.demo {
background-color: skyblue;
}
</style>
使用了scoped
之后,SiteUser
和SiteUser
中的.demo
样式就不会互相冲突。因为它们的.demo
仅作用于当前.vue
文件的模板中,不会作用于其它组件,对其它组件造成影响。
注:
一般不会在
App.vue
中使用scoped
样式。因为App.vue
管理着整个项目中所有的组件。所以在App.vue
中定义的样式一般是要全局生效的样式。如果在App.vue
中使用scoped
,那么App.vue
中的样式只会对App.vue
中的非组件元素生效。也就是说,使用了
scoped
的组件,其样式一般不会对子组件生效。
Scoped 原理
使用了scoped
的组件,Vue会在其真实DOM中的所有非组件元素上添加一个随机的、以data-v-
为开头的属性(子组件只会在其根元素上添加这个data-v-
属性)。然后Vue会对该组件的所有样式都加上一个属性选择器,例如.demo[data-v-xxxxxxxx]
。如此一来,使用了scoped
的组件,它们的样式不会被作用到其它元素上。
以上例中的两个组件为例,在App.vue
使用它们,并且使用scoped
:
<!--
App.vue
-->
<template>
<div class="demo">
<div><h1>{{msg}}</h1></div>
<hr>
<MySite/>
<hr>
<SiteUser/>
</div>
</template>
<script>
import MySite from './components/MySite.vue'
import SiteUser from './components/SiteUser.vue';
export default {
name: 'app',
data() {
return {
msg: 'Hello!'
}
},
components: {
SiteUser,
MySite,
},
}
</script>
<style scoped>
.demo {
color: red;
}
</style>
<!--
SiteUser.vue
-->
<template>
<div class="demo">
<h2 class="title">用户姓名:{{name}}</h2> <!-- App.vue 中的 .title 不会对其生效 -->
<h2>用户年龄:{{age}}</h2>
<h2>用户性别:{{sex}}</h2>
</div>
</template>
在浏览器中,生成的真实DOM可能是这样的:
<div data-v-7bxxxxxx="">
<div data-v-7bxxxxxx="">
<h1 data-v-7bxxxxxx="" class="title">Hello!</h1>
</div>
<hr data-v-7bxxxxxx="">
<div data-v-2fxxxxxx="" data-v-7bxxxxxx="" class="demo">
<h2 data-v-2fxxxxxx="">网站名称:Linner's Blog</h2>
<h2 data-v-2fxxxxxx="">网站地址:blog.linner.asia</h2>
</div>
<hr data-v-7bxxxxxx="">
<div data-v-5axxxxxx="" data-v-7bxxxxxx="" class="demo">
<h2 data-v-5axxxxxx="" class="title">用户姓名:张三</h2>
<h2 data-v-5axxxxxx="">用户年龄:21</h2>
<h2 data-v-5axxxxxx="">用户性别:男</h2>
</div>
</div>
评论