Appearance
构建工具调用相应的处理器(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}样式穿透不仅可以穿透子组件,还可以穿透深层的后代组件