scoped的作用:
在vue组件中,为了使样式私有化(模块化),不对全局造成污染,可以在 style 标签上添加scoped 属性以表示它的只属于当下的模块,这是一个非常好的举措,但是为什么要慎用呢?因为在我们需要修改公共组件(三方库或者项目定制的组件)的样式的时候,scoped 往往会造成更多的困难,需要增加额外的复杂度。
scoped 实现样式私有化原理
通过查看 DOM 结构发现:vue 通过在 DOM 结构以及 css 样式上加唯一不重复的标记,以保证唯一,达到样式私有化模块化的目的。
注意:当 <style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素。
这类似于 Shadow DOM 中的样式封装。它有一些注意事项,但不需要任何 polyfill。它通过使用 PostCSS 来实现以下转换:
转换之前
<template>
<div class="example">hi</div>
</template>
<style scoped>
.example {
color: red;
}
</style>
转换之后
<template>
<div class="example" data-v-f3f3eg9>hi</div>
</template>
<style>
.example[data-v-f3f3eg9] {
color: red;
}
</style>
为何慎用
从上面可以看出,添加了 scoped 属性的组件,为了达到组件样式模块化,做了两个处理:
- 给 HTML 的 DOM 节点加一个不重复 data 属性(形如:data-v-2311c06a )来表示他的唯一性
- 在每句 css 选择器的末尾(编译后生成的 css 语句)加一个当前组件的 data 属性选择器(如[data-v-2311c06a])来私有化样式。
我们都知道 css 选择器有优先级,scoped 的这一波操作,虽然实现了组件样式模块化的目的,但是每个样式的权重加重了,这意味着若要修改这个样式,需要更高的权重去覆盖这个样式。这是增加复杂度的其中一个维度。
深度作用选择器
如果你希望 scoped 样式中的一个选择器能够作用得“更深”,例如影响子组件,你可以使用 >>> 操作符:
<style scoped>
.a >>> .b { /* ... */ }
</style>
上述代码将会编译成:
.a[data-v-f3f3eg9] .b { /* ... */ }
有些像 Sass 之类的预处理器无法正确解析 >>>
。这种情况下你可以使用 /deep/
操作符取而代之——这是一个 >>>
的别名,同样可以正常工作。
eg:
<style scoped>
.q-tab /deep/ ul {
padding-top: 20px;
}
</style>
总结scoped的渲染规则
- 给 HTML 的 DOM 节点加一个不重复 data 属性(形如:data-v-2311c06a)来表示他的唯一性
- 在每句 css 选择器的末尾(编译后的生成的 css 语句)加一个当前组件的 data 属性选择器(如 [data-v-2311c06a] )来私有化样式
- 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性:
1、当父组件样式为scoped,且子组件样式也为scoped,父组件会给子组件的最外层标签加上
当前组件的data属性,在父组件中只能修改子组件根标签的样式,重写不了组件内部的样式,
可以使用`深度作用选择器`。
2、当父组件样式为全局样式,且子组件样式为scoped,或者,父子组件均为全局组件,
父组件内可以修改子组件的样式,比权重。
总结:当 <style> 标签有 scoped 属性时,该组件的样式是私有样式,只作用于该组件不影响其他组件(尤大大)。
推荐方案
混用本地和全局样式
你可以在一个组件中同时使用有 scoped 和非 scoped 样式:
<style>
/*全局样式*/
</style>
<style scoped>
/*本地样式*/
</style>