样式冲突
Vue多组件开发时,组件中的CSS样式默认是全局使用的。例如在A.vue这个组件中定义的样式,也会在B.vue这个组件中生效。
而当项目中使用的组件越来越多时,这种全局通过的CSS样式的方式势必会影响整个项目,导致样式冲突。例如A.vue和B.vue都用了.demo这个样式,那么在引入和使用组件时,会造成样式的冲突。相同选择器的样式,究竟是哪个组件中的样式生效?
Vue在引入组件.vue时,如果存在相同的样式导致的冲突,后引入的组件会覆盖先引入的组件中相同的样式。例如A.vue和B.vue都用了.demo这个样式:
1import A from './components/A.vue'
2import B from './components/B.vue'
3// B 中的 .demo 会覆盖 A 中的 .demo,并全局生效
Scoped
为了解决这种样式冲突的问题,Vue引入了scoped(样式范围)。使用scoped可以让当前组件中的样式,仅对当前组件模板中的元素生效(样式局部生效,防止冲突)。
scoped的用法是,在.vue中,直接作用在<style>标签上:
1<style scoped>
2 /* ... */
3</style>
例如:
1<template>
2<div class="demo">
3 <h2>用户姓名:{{name}}</h2>
4 <h2>用户年龄:{{age}}</h2>
5 <h2>用户性别:{{sex}}</h2>
6</div>
7</template>
8
9<script>
10export default {
11 name: 'SiteUser',
12 data() {
13 return {
14 name: '张三',
15 sex: '男',
16 age: 21,
17 }
18 },
19}
20</script>
21
22<style scoped>
23.demo {
24 background-color: orange;
25}
26</style>
1<template>
2<div class="demo">
3 <h2>网站名称:{{name}}</h2>
4 <h2>网站地址:{{url}}</h2>
5</div>
6</template>
7
8<script>
9export default {
10 name: 'MySite',
11 data() {
12 return {
13 name: 'Linner\'s Blog',
14 url: 'blog.linner.asia',
15 }
16 },
17};
18</script>
19
20<style scoped>
21.demo {
22 background-color: skyblue;
23}
24</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:
1<!--
2 App.vue
3 -->
4
5<template>
6 <div class="demo">
7 <div><h1>{{msg}}</h1></div>
8 <hr>
9 <MySite/>
10 <hr>
11 <SiteUser/>
12 </div>
13</template>
14
15<script>
16 import MySite from './components/MySite.vue'
17 import SiteUser from './components/SiteUser.vue';
18
19 export default {
20 name: 'app',
21 data() {
22 return {
23 msg: 'Hello!'
24 }
25 },
26 components: {
27 SiteUser,
28 MySite,
29 },
30 }
31</script>
32
33<style scoped>
34.demo {
35 color: red;
36}
37</style>
1<!--
2 SiteUser.vue
3 -->
4<template>
5<div class="demo">
6 <h2 class="title">用户姓名:{{name}}</h2> <!-- App.vue 中的 .title 不会对其生效 -->
7 <h2>用户年龄:{{age}}</h2>
8 <h2>用户性别:{{sex}}</h2>
9</div>
10</template>
在浏览器中,生成的真实DOM可能是这样的:
1<div data-v-7bxxxxxx="">
2 <div data-v-7bxxxxxx="">
3 <h1 data-v-7bxxxxxx="" class="title">Hello!</h1>
4 </div>
5 <hr data-v-7bxxxxxx="">
6 <div data-v-2fxxxxxx="" data-v-7bxxxxxx="" class="demo">
7 <h2 data-v-2fxxxxxx="">网站名称:Linner's Blog</h2>
8 <h2 data-v-2fxxxxxx="">网站地址:blog.linner.asia</h2>
9 </div>
10 <hr data-v-7bxxxxxx="">
11 <div data-v-5axxxxxx="" data-v-7bxxxxxx="" class="demo">
12 <h2 data-v-5axxxxxx="" class="title">用户姓名:张三</h2>
13 <h2 data-v-5axxxxxx="">用户年龄:21</h2>
14 <h2 data-v-5axxxxxx="">用户性别:男</h2>
15 </div>
16</div>
评论