Vue中样式scoped和样式穿透的实现原理
在vue组件中可以使用scoped的css来实现样式的模块化,避免对其他组件的影响;而想在父组件中修改子组件的样式时往往由于scoped的原因,导致样式不生效,需要使用深度选择器进行样式穿透。
那么本文就来看看具体是怎么实现的。
1.准备案例
父组件App.vue
<template> <div class="container"> <h3 class="title">Hello Vue</h3> <card /> </div> </template> <script> import Card from './Card.vue'; export default { components: { Card } } </script> <style scoped> .container { width: 300px; height: 300px; padding-top: 10px; background-color: #efefef; } .container .title { color: #709b90; text-align: center; } .card { border: 2px solid#CDC9C9; margin-left: 50px; } </style>
子组件Card.vue
<template> <div class="card"> <div class="title">Vue Cli</div> <div class="list"> <div class="item">vue serve</div> <div class="item">vue build</div> </div> </div> </template> <style scoped> .card { width: 200px; height: 200px; } .title { height: 30px; line-height: 30px; color: #FFDEAD; padding-left: 10px; background-color: #87CEFA; } .item { height: 30px; line-height: 30px; padding-left: 10px; background-color: #FFE4E1; } </style>
效果如下:
2.scoped的实现
把组件进行编译,查看一下:
编译后的html:
<div data-v-4fe14a3c="" class="container"> <h3 data-v-4fe14a3c="" class="title">Hello Vue</h3> <div data-v-119ff0e6="" data-v-4fe14a3c="" class="card"> <div data-v-119ff0e6="" class="title">Vue Cli</div> <div data-v-119ff0e6="" class="list"> <div data-v-119ff0e6="" class="item">vue serve</div> <div data-v-119ff0e6="" class="item">vue build</div> </div> </div> </div>
需要说明一下:每次编译后vue组件中的dom会由js动态生成,所以需要用浏览器打开编译后的html,再用开发者工具查看
编译后的css:
.card[data-v-119ff0e6] { width: 200px; height: 200px } .title[data-v-119ff0e6] { height: 30px; line-height: 30px; color: #ffdead; padding-left: 10px; background-color: #87cefa } .item[data-v-119ff0e6] { height: 30px; line-height: 30px; padding-left: 10px; background-color: #ffe4e1 } .container[data-v-4fe14a3c] { width: 300px; height: 300px; padding-top: 10px; background-color: #efefef } .container .title[data-v-4fe14a3c] { color: #709b90; text-align: center } .card[data-v-4fe14a3c] { border: 2px solid#CDC9C9; margin-left: 50px }
编译后dom元素都会添加上data-v-hashxxxx的属性,而样式中的选择器也被添加上了data-v-hashxxxx的属性,每个组件的data-v-hashxxxx是一样的因此限制了样式的作用范围。另外子组件的最外层也会被添加上父组件的data-v-hashxxxx,同时含有父组件、子组件的data-v-hashxxxx属性,所以父组件中的样式会对子组件的最外层元素起作用,如上面的.card定义的样式。
3.父组件中直接修改子组件的样式
App.vue中添加:
.card .title { color: #eee; }
在父组件中直接修改子组件中(非最外层)元素的样式,然而并不起作用,来看看怎么回事吧。
再次编译,查看编译后的html:
<div data-v-7a169200="" class="container"> <h3 data-v-7a169200="" class="title">Hello Vue</h3> <div data-v-119ff0e6="" data-v-7a169200="" class="card"> <div data-v-119ff0e6="" class="title">Vue Cli</div> <div data-v-119ff0e6="" class="list"> <div data-v-119ff0e6="" class="item">vue serve</div> <div data-v-119ff0e6="" class="item">vue build</div> </div> </div> </div>
编译后的css:
.card[data-v-119ff0e6] { width: 200px; height: 200px } .title[data-v-119ff0e6] { height: 30px; line-height: 30px; color: #ffdead; padding-left: 10px; background-color: #87cefa } .item[data-v-119ff0e6] { height: 30px; line-height: 30px; padding-left: 10px; background-color: #ffe4e1 } .container[data-v-7a169200] { width: 300px; height: 300px; padding-top: 10px; background-color: #efefef } .container .title[data-v-7a169200] { color: #709b90; text-align: center } .card[data-v-7a169200] { border: 2px solid#CDC9C9; margin-left: 50px } .card .title[data-v-7a169200] { color: #eee }
在父组件内编写的样式经编译后都会带上和父组件元素一样的data-v-hashxxxx,只对父组件范围内的元素起作用,触及不到引入的子组件的内部,也就是说无法覆盖子组件的样式。
4.使用 >>> 深度选择器进行样式穿透
在App.vue中添加样式,修改子组件的title及列表项的字体颜色:
.card >>> .title { color: #eee; } .container .card >>> .list .item { color: #FF6347; }
这次起作用了,效果如下:
再查看一下编译后的结果,编译后的html:
<div data-v-dba577b2="" class="container"> <h3 data-v-dba577b2="" class="title">Hello Vue</h3> <div data-v-119ff0e6="" data-v-dba577b2="" class="card"> <div data-v-119ff0e6="" class="title">Vue Cli</div> <div data-v-119ff0e6="" class="list"> <div data-v-119ff0e6="" class="item">vue serve</div> <div data-v-119ff0e6="" class="item">vue build</div> </div> </div> </div>
编译后的css:
.card[data-v-119ff0e6] { width: 200px; height: 200px } .title[data-v-119ff0e6] { height: 30px; line-height: 30px; color: #ffdead; padding-left: 10px; background-color: #87cefa } .item[data-v-119ff0e6] { height: 30px; line-height: 30px; padding-left: 10px; background-color: #ffe4e1 } .container[data-v-dba577b2] { width: 300px; height: 300px; padding-top: 10px; background-color: #efefef } .container .title[data-v-dba577b2] { color: #709b90; text-align: center } .card[data-v-dba577b2] { border: 2px solid#CDC9C9; margin-left: 50px } .card[data-v-dba577b2] .title { color: #eee } .container .card[data-v-dba577b2] .list .item { color: tomato }
可以看到,使用样式穿透后编译后没有在选择器末尾添加data-v-hashxxxx属性,而是把data-v-hashxxxx添加到了>>>的位置,这样就能够选中子组件中的元素了。
补充一下
不同css预处理器中样式穿透的写法:
css: >>>
less/sass: /deep/
scss: ::v-deep