scoped的原理

Vue Scoped样式作用域隔离原理、编译产物、深度选择器与其他方案对比

#tech / dev / frame #type / concept #status / growing

Scoped 样式的作用域原理

Vue 中的 scoped 属性是通过 PostCSS 插件在编译阶段对 CSS 进行特殊处理,实现组件级样式隔离的核心机制。

三步机制

  1. 唯一属性标记生成 — Vue Loader 基于组件文件路径生成唯一哈希值,如 data-v-f3242
  2. DOM 元素标记 — 编译时,该属性自动添加到组件模板的所有 HTML 元素上
  3. 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 CSSCSS Modules
隔离方式属性选择器 [data-v-xxx]唯一类名哈希化
使用方式<style scoped><style module> + $style.className
性能属性选择器略慢于类选择器类选择器匹配更快
穿透样式:deep()手动组合类名
适用场景组件内部样式隔离大型项目、CSS-in-JS 习惯

性能考量

  • 属性选择器 [data-v-xxx] 的匹配速度略低于类选择器,但实际影响很小
  • 避免过度嵌套 :deep(),增加选择器特异性会降低可维护性
  • 大型项目中 CSS Modules 的哈希类名在选择器性能上更优
创建于 2025/1/1 更新于 2026/5/27