Vue 2 中的 Scoped CSS 是通过 PostCSS 插件 postcss-scoped
实现的,其核心原理是通过为组件添加唯一属性标识 并重写 CSS 选择器来实现样式隔离。以下是详细原理:
实现步骤:
-
添加唯一属性标识
当在
<style>
标签中添加scoped
属性后,Vue 在编译时会为当前组件的所有 DOM 元素 添加一个唯一的data-v-xxxxxxx
属性(如data-v-9ea40744
)。该哈希值基于组件路径生成,确保全局唯一。 -
重写 CSS 选择器
同时,Vue 会重写
<style scoped>
内的所有 CSS 规则 ,在每个选择器末尾添加属性选择器[data-v-xxxxxxx]
。例如:
css
/* 原始样式 */
.home {
background: red;
}
.img {
width: 100%;
}
编译后变为:
css
.home[data-v-9ea40744] {
background: red;
}
.img[data-v-9ea40744] {
width: 100%;
}
最终效果:
html
<!-- 编译后的模板 -->
<div data-v-9ea40744="" class="home">
<img data-v-9ea40744="" alt="Vue logo" src="" class="img">
</div>
<!-- 编译后的样式 -->
<style>
.home[data-v-9ea40744] {
background: red;
}
.img[data-v-9ea40744] {
width: 100%;
}
</style>
- 样式仅作用于当前组件 :由于其他组件的 DOM 元素没有相同的
data-v-xxxxxxx
属性,样式不会泄露。 - 不影响全局样式 :未添加
scoped
的样式仍为全局样式。
深度选择器(穿透作用域)
当需要影响子组件样式时,使用深度选择器:
css
/* 写法1:Vue 2 推荐 */
.parent >>> .child { color: blue; }
/* 写法2:Sass/预处理器可用 */
.parent /deep/ .child { color: blue; }
/* 写法3:Vue 3 也兼容的语法 */
.parent ::v-deep .child { color: blue; }
编译后:
css
.parent[data-v-f3f3eg9] .child { color: blue; }
注意:深度选择器会移除子组件选择器前的属性限定,使样式穿透到子组件。
关键特点:
- 局部作用域
样式仅影响当前组件,避免全局污染。 - 属性选择器实现
不依赖 CSS Modules 的类名哈希,而是通过 HTML 属性隔离。 - 编译时处理
在.vue
文件编译阶段由vue-loader
完成转换。 - 权重不变
选择器权重与原始规则一致(如.class[attr]
权重 = 类选择器 + 属性选择器)。
注意事项:
- 不适用于动态内容 :通过
v-html
插入的 DOM 不会添加data-v-xxxxxxx
属性,需手动处理或使用深度选择器。 - 性能影响:属性选择器比类选择器稍慢,但现代浏览器中差异可忽略。
- 避免全局样式泄漏:Scoped 样式依然可能被全局样式覆盖(因 CSS 层叠规则)。
通过这种巧妙的属性选择器重写机制,Vue 2 实现了简洁高效的 CSS 作用域隔离。