Skip to content

构建工具调用相应的处理器(loader),当 SFC 组件是 style 标签包含 scoped 属性时,处理器会给组件的 DOM 节点和子组件添加自定义属性data-v-xxx,子组件只会在最外层 DOM 节点添加自定义属性data-v-xxx,然后会给组件内 CSS 选择器之后添加对应的属性选择器[data-v-xxx]

举例说明

父组件 template 内容:

Vue
<template>
    <div>
        <span>Hello World! I'm father!</span>
        <span>I love coding.</span>
        <SonComponent></SonComponent>
    </div>
</template>
<style>
span {
    color: red;
    }
.hobby{
    background-color: yellowgreen;
    }
</style>
<template>
    <div>
        <span>Hello World! I'm father!</span>
        <span>I love coding.</span>
        <SonComponent></SonComponent>
    </div>
</template>
<style>
span {
    color: red;
    }
.hobby{
    background-color: yellowgreen;
    }
</style>

子组件 template 内容:

Vue
<template>
    <div>
        <div>Hello, I'm son!</div>
        <div class="hobby">I love painting.</div>
    </div>
</template>
<style>
div {
    color: blue;
}
.hobby{
    background-color: pink;
}

</style>
<template>
    <div>
        <div>Hello, I'm son!</div>
        <div class="hobby">I love painting.</div>
    </div>
</template>
<style>
div {
    color: blue;
}
.hobby{
    background-color: pink;
}

</style>

如果父组件和子组件都没有在 style 标签上添加 scoped 属性,渲染后的结果是:

HTML
<div>
    <span>Hello World! I'm father!</span>
    <span>I love coding.</span>
    <div>
        <div>Hello, I'm son!</div>
        <div>I love painting.</div>
    </div>
</div>
<div>
    <span>Hello World! I'm father!</span>
    <span>I love coding.</span>
    <div>
        <div>Hello, I'm son!</div>
        <div>I love painting.</div>
    </div>
</div>

给父组件和子组件 style 标签添加 scoped 属性,渲染的结果是:

HTML
<div data-v-938b83b0>
    <span data-v-938b83b0>Hello World! I'm father!</span>
    <span data-v-938b83b0>I love coding.</span>
    <div data-v-938b83b0 data-v-fb8c5270>
        <div data-v-fb8c5270>Hello, I'm son!</div>
        <div data-v-fb8c5270>I love painting.</div>
    </div>
</div>
<div data-v-938b83b0>
    <span data-v-938b83b0>Hello World! I'm father!</span>
    <span data-v-938b83b0>I love coding.</span>
    <div data-v-938b83b0 data-v-fb8c5270>
        <div data-v-fb8c5270>Hello, I'm son!</div>
        <div data-v-fb8c5270>I love painting.</div>
    </div>
</div>
CSS
div[data-v-f2eb8ef8]{color:#00f}
.hobby[data-v-f2eb8ef8]{background-color:pink}
span[data-v-080e8582]{color:red}
.hobby[data-v-080e8582]{background-color:#9acd32}
div[data-v-f2eb8ef8]{color:#00f}
.hobby[data-v-f2eb8ef8]{background-color:pink}
span[data-v-080e8582]{color:red}
.hobby[data-v-080e8582]{background-color:#9acd32}

此时,如果想修改子组件的样式,只能修改子组件最外层元素样式,无法修改子组件内部的元素的样式,因为父组件中,所有 CSS 选择器的后面都添加了父组件的自定义属性data-v-f2eb8ef8,只有拥有属性data-v-f2eb8ef8的元素才能应用父组件 CSS 选择器中的样式,子组件中除了子组件最外层元素,都没有data-v-f2eb8ef8属性。

要想改变子组件内部的样式,在 vue3 中需要在选择器使用:deep()包裹,生成的 CSS 会将data-v-f2eb8ef8提到选择器前方,而不是后方,此时,子组件最外层元素是有data-v-f2eb8ef8属性的,因此能够实现选择子组件的元素,实现样式穿透。

CSS
div[data-v-f2eb8ef8] {color:#00f}
.hobby[data-v-f2eb8ef8] {background-color:pink}
span[data-v-0a512275] {color:red}
[data-v-0a512275] .hobby{background-color:#9acd32}
div[data-v-f2eb8ef8] {color:#00f}
.hobby[data-v-f2eb8ef8] {background-color:pink}
span[data-v-0a512275] {color:red}
[data-v-0a512275] .hobby{background-color:#9acd32}

样式穿透不仅可以穿透子组件,还可以穿透深层的后代组件