vue的scoped样式通过编译时为选择器和元素添加唯一data-v-xxx属性实现伪作用域隔离,非真正的shadow dom;穿透需用:deep(),且仅括号内选择器生效。

Vue单文件组件里
它不是简单地给元素加个属性选择器,而是让CSS只作用于当前组件的根元素及其子树——但这个“只作用”是靠编译时加属性选择器模拟出来的,不是运行时隔离。
编译后,<style scoped></style> 里的 .btn 会变成类似 .btn[data-v-f3f518dc],同时 Vue 会给组件所有顶层元素自动加上 data-v-f3f518dc 属性。所以它本质是 CSS 层面的“作用域伪装”,不是 Shadow DOM 那种真正隔离。
- 子组件的根元素也会被自动打上这个
data-v-xxx属性,但它的内部元素不会——除非你显式用deep或:deep() - 如果子组件用了
inheritAttrs: false,又没手动把$attrs挂到根元素上,那个data-v-xxx属性可能就丢了,导致样式失效 -
<style scoped></style>对<style></style>标签外的全局样式、CSS-in-JS、或第三方库注入的样式完全没影响
想穿透scoped样式改子组件内部元素,该用 :deep() 还是 /deep/
/deep/ 是老写法,Vue CLI 4.5+ 和 Vite 默认已弃用;现在统一用 :deep()(函数调用语法),它明确告诉编译器:这里的内容不加 data-v-xxx 属性前缀。
注意 :deep() 只能用在 <style scoped></style> 内部,且只对括号里的选择器生效,外面的仍受 scoped 约束。
立即学习“前端免费学习笔记(深入)”;
启山智软物流配送是基于Spring Cloud 和 Vue.js的JAVA物流配送系统。包含总控制后台 、城市合伙人(商家pc端)、 区域团长后台 、用户端小程序 、手机H5等多个操作模块。为响应用户需求我们新增了后台自定义装修组件模块,使页面更加美观,操作更加灵活简便。淘宝商品CSV一键导入,提升用户使用感。还有与众不同的管理台侧边栏设计,打破传统管理台样式。 另有公众号接龙、引导页上传、区域团
- 写法:
:deep(.el-input__inner) { border: 2px solid red; }—— 只有.el-input__inner会穿透,前面的父选择器依然带data-v-xxx - 不能写成
:deep(.parent .el-input__inner)然后指望整个链路都穿透;只有括号里那部分被豁免 - Vite + Vue 3 中,
::v-deep()已彻底移除,强行用会报错;/deep/虽然部分构建工具还兼容,但属于技术债
scoped样式和CSS Modules谁更适合大型项目
Vue 的 scoped 是轻量级、零配置的局部化方案;CSS Modules 是更严格的模块化系统,需构建工具支持,生成的类名更可控,但要多写 import styles from './xxx.module.css' 和 :class="styles.xxx"。
如果你的项目已经重度依赖 class 绑定、动态 class 切换、或需要服务端渲染时保证 class 名稳定,CSS Modules 更可靠;但若只是避免样式污染、团队习惯直接写 class="xxx",scoped 足够用。
-
scoped编译出的data-v-xxx属性在 SSR 中也存在,但 class 名本身不唯一,不利于缓存和调试 - CSS Modules 的 class 名默认带哈希(如
Button_button__aBc12),天然支持按需导出、类型提示(配合@types/css-modules) - 两者不互斥:可以
<style module></style>和<style scoped></style>并存,但一般不这么干——语义冲突
为什么有时候scoped样式突然不生效了
最常见原因不是写错了,而是组件结构或渲染时机导致属性没挂上。
比如异步加载子组件、使用 v-if 切换、或子组件根节点是 <teleport></teleport> 目标容器——这些场景下,data-v-xxx 属性可能根本没出现在目标 DOM 上。
- 检查浏览器开发者工具里对应元素有没有
data-v-xxx属性;没有就说明 Vue 没把它当本组件 DOM 渲染 -
<teleport to="#modal-root"></teleport>内的内容完全脱离当前组件作用域,scoped样式对其无效,必须用:deep()+ 全局样式配合,或单独抽离 CSS - 使用
defineAsyncComponent时,如果异步组件还没加载完成,占位用的<div> 可能没带 <code>data-v-xxx,等真实组件挂载后才补上——此时样式会闪一下scoped 的边界感很弱,它依赖 Vue 的模板编译和 DOM 注入逻辑;一旦跳出这个路径,就得靠手动控制或换方案。









