scoped的原理
Vue Scoped样式作用域隔离原理、编译产物、深度选择器与其他方案对比
#tech / dev / frame
#type / concept
#status / growing
Scoped 样式的作用域原理
Vue 中的 scoped 属性是通过 PostCSS 插件在编译阶段对 CSS 进行特殊处理,实现组件级样式隔离的核心机制。
三步机制
- 唯一属性标记生成 — Vue Loader 基于组件文件路径生成唯一哈希值,如
data-v-f3242 - DOM 元素标记 — 编译时,该属性自动添加到组件模板的所有 HTML 元素上
- CSS 选择器转换 — 为组件内所有 CSS 选择器追加
[data-v-xxxxxx]属性选择器
核心思想:使选择器唯一
编译产物示例
源码:
<template>
<div class="title">Hello</div>
</template>
<style scoped>
.title { color: red; }
</style>
编译后 HTML:
<div class="title" data-v-7ba5bd90>Hello</div>
编译后 CSS:
.title[data-v-7ba5bd90] { color: red; }
深度选择器
修改子组件或第三方组件样式时,scoped 会阻止穿透,需要深度选择器:
<style scoped>
/* Vue 3 推荐写法 */
:deep(.el-input__inner) {
border-color: blue;
}
/* Vue 2 / 旧写法(已废弃) */
::v-deep .el-input__inner { border-color: blue; }
/deep/ .el-input__inner { border-color: blue; }
</style>
:deep() 编译后:.parent[data-v-xxx] .el-input__inner(父级带 scoped 标记,子级不带)。
:slotted()
针对 <slot> 插入内容的样式:
<style scoped>
:slotted(.slot-item) {
color: blue;
}
</style>
仅对当前组件的直接插槽内容生效,不影响嵌套组件。
:global()
取消 scoped 限制,全局生效:
<style scoped>
:global(.ant-modal-mask) {
background: rgba(0, 0, 0, 0.5);
}
</style>
编译后不带 [data-v-xxx] 属性选择器。
与 CSS Modules 对比
| 特性 | Scoped CSS | CSS Modules |
|---|---|---|
| 隔离方式 | 属性选择器 [data-v-xxx] | 唯一类名哈希化 |
| 使用方式 | <style scoped> | <style module> + $style.className |
| 性能 | 属性选择器略慢于类选择器 | 类选择器匹配更快 |
| 穿透样式 | :deep() | 手动组合类名 |
| 适用场景 | 组件内部样式隔离 | 大型项目、CSS-in-JS 习惯 |
性能考量
- 属性选择器
[data-v-xxx]的匹配速度略低于类选择器,但实际影响很小 - 避免过度嵌套
:deep(),增加选择器特异性会降低可维护性 - 大型项目中 CSS Modules 的哈希类名在选择器性能上更优