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

 
posted on 2021-05-26 14:04  橘生淮南_  阅读(1349)  评论(0编辑  收藏  举报