Vue scoped 和 deep 原理

在没有 scoped 时,我们写的所有样式都是全局样式,一旦类名重复,样式就会被更改,造成污染。scope 的作用就是防止样式污染。

scoped 原理

vue 中 scoped 主要是通过 postcss 实现的,转译代码时做了如下处理:

  • 给每个 DOM 节点添加一个唯一的属性(如:data-v-a0901)
  • 在 css 选择器后面加上属性选择器(如:.box[data-v-a0901])来私有化样式

如果组件内包含子组件,只会给子组件的最外层标签加上当前组件的 data-v 属性。
如果子组件是第三方组件,那么其最外层标签可能添加不上 data-v 属性。

转译前:

1
2
3
4
5
6
7
<div class="box">xxx</div>

<style scoped>
.box {
color: red;
}
</style>

转译后:

1
2
3
4
5
6
7
<div data-v-a0901 class="box">xxx</div>

<style scoped>
.box[data-v-a0901] {
color: red;
}
</style>

scoped 产生的影响

一般情况下,有修改其他组件样式的需求时,大多是父组件修改子组件的样式,尤其是在项目中使用了第三方组件库,我们经常需要去修改他们的样式。

然而,由于组件的样式添加了属性选择器,这就会导致修改不成功。

父组件:

1
2
3
4
5
<template>
<div class="parent">
<child />
</div>
</template>

子组件:

1
2
3
4
5
<template>
<div class="child">
<div class="child-1" />
</div>
</template>

编译后:

1
2
3
4
5
<div data-v-8dea36e1 class="parent">
<div data-v-8eee7a9c data-v-8dea36e1 class="child">
<div data-v-8eee7a9c class="child-1"></div>
</div>
</div>

如果在父组件中修改 .child,编译后的样式为:

1
.child[data-v-8dea36e1] {}

由于 .child 所在标签有 data-v-8dea36e1 属性,修改成功。

如果在父组件中修改 .child-1,编译后的样式为:

1
.child-1[data-v-8dea36e1] {}

由于 .child-1 所在标签没有 data-v-8dea36e1 属性,修改失败。

此时想要修改成功,就需要用 :deep() 穿透。

deep 深度选择器

deep 的原理就是将 css 选择器的私有属性由子组件变为父组件。

在父组件中用 deep 修改子组件样式:

1
:deep(.child-1) {}

编译后:

1
[data-v-8dea36e1] .child-1 {}

将属性选择器提前,并将属性改为父组件私有属性。
现在他的匹配顺序是:先匹配带有 data-v-8dea36e1 属性的标签,然后再匹配该标签下拥有类名为 child-1 的标签。
因此可以修改成功。