Vue 的 scoped 样式是什么原理?为什么修改子组件样式需要 :deep()?
下面是一段教学用的模拟面试。
🧑💻 面试官:Vue 的 scoped,是把 CSS 放进 Shadow DOM 吗?
🙋♂️ 我:通常不是。编译器会给元素加作用域标识,再改写选择器。
🧑💻 面试官:父组件有 .title,子组件内部也有 .title,为什么没有一起变色?
🙋♂️ 我:子组件内部元素通常没有父组件要求的那个作用域标识。
🧑💻 面试官:那子组件根节点为什么又能受父样式影响?用 :deep() 之后,会不会把整个网站都改了?
看浏览器最终匹配的选择器,比记“样式只在当前组件生效”更准确。
面试速答(60 秒版)
Vue 单文件组件的 scoped,主要靠编译后的属性选择器限定样式范围。组件元素带有对应的作用域属性,CSS 选择器也会加入这个条件,所以普通样式不会随意匹配其他组件的内部元素。
父组件样式仍可以影响子组件根节点,这是为了让父组件安排子组件在页面中的布局。根节点与子组件内部后代,需要分开理解。
如果确实要修改后代内部样式,可以使用 :deep()。它让指定的后代选择部分不再要求当前组件的作用域属性,但前面的外层范围仍然可以保留。
因此,deep 应该配合明确的外层容器和稳定的组件接口使用。它不是全局重置开关,也不是安全隔离机制。能通过组件属性、主题变量或插槽完成的改动,优先使用这些公开接口。

知识点详解:样式没有进去,是哪项匹配条件没对上?
scoped 编译以后是什么?
假设一个组件内部有 class 为 title 的标题。编译后,元素可能多出一个 data-v-xxxx 属性,具体值由构建产生。
对应的样式可以示意为:
/* 编译前 */
.title { color: red; }
/* 编译后示意,标识不是固定值 */
.title[data-v-parent] { color: red; }
浏览器仍在执行普通 CSS 匹配。它不会因为文件名属于某个组件,就自动理解组件边界,而是检查类名与属性是否满足。
所以,scoped 不是 Shadow DOM,也不会阻止全局 CSS 根据其他匹配条件影响这个元素。
子组件根节点,为什么是例外?
假设父组件的 panel 里放了一个子组件。父组件需要给子组件安排外边距、宽度等布局,这些作用在根节点上。
Vue 让子组件根节点可以同时带有父、子两边的相关作用域标识,因此根节点能够受到两边 scoped 样式影响。子组件内部更深的 title 元素,则通常只属于子组件自己的标识范围。
看到根节点可以匹配父样式,不能推出内部所有元素都可以。官方说明见 Vue SFC CSS 文档。
:deep() 放开的是哪一部分?
假设父组件想修改 panel 内子组件的 title:
/* 写在父组件的 scoped 样式中 */
.panel :deep(.title) { color: red; }
/* 编译后的选择器示意 */
.panel[data-v-parent] .title { color: red; }
外层 panel 仍然要求父组件的作用域标识,后面的 title 不再要求这个标识。只要它是匹配外层容器的后代,就有机会被选中。
因此它仍有范围,但范围由你写下的外层选择器决定。只写一个很宽泛的 deep,可能把当前范围里的多个嵌套组件都选进去。递归组件还需要特别检查实际 DOM。
这部分属于 Vue/CSS 编译规则,没有官方 Python 版本。CSS 与最终 DOM 已能说明机制,不需要翻译成另一个语言的伪 API。
v-html 和插槽,也按同一规则处理吗?
v-html 插入的 DOM 不是通常的模板编译结果,内部内容不会自动拿到当前组件的作用域属性。可以通过有范围的 deep 来匹配,但内容来源的安全检查仍然是另一件事,scoped 不会过滤恶意 HTML。
插槽内容则涉及内容归属。要从当前组件样式明确选择插入的槽内容,可以使用 :slotted();不要把它和 deep 一律互换。
需要局部声明全局选择器时,还有 :global()。它们各自控制选择器范围,不是三种写法随便挑一种。
第三方组件,直接 deep 内部类名好吗?
可以作为必要的覆盖手段,但会依赖对方的内部结构。组件升级后换了类名、增加包装层或使用特殊渲染位置,原选择器就可能失效。
如果组件提供主题变量、尺寸属性或样式接口,用这些方式更容易维护。确实要覆盖内部样式,则收窄外层范围,把选择器与组件版本一起测试。
面试回答到这里,就不只是“加个 deep 就好了”,而是说明了修改在哪里生效,以及以后为什么会坏。

面试官继续追问
Scoped 样式是不是不会发生冲突?
不是。全局规则、继承、优先级和子组件根节点等仍然参与 CSS 计算。作用域条件减少误匹配,不改变整个级联系统。
Deep 能影响弹到 body 下的内容吗?
选择器仍按实际 DOM 后代关系匹配。内容被移到外层容器之外,原来的后代选择器未必能选到;要看组件提供的专门接口和真实结构。
怎么排查“写了 deep 还是没效果”?
在开发者工具里看实际 DOM、编译后的选择器、匹配结果和被覆盖的规则。先确认是否选中,再判断优先级,别一开始就不断增加 !important。
面试速记卡
- scoped 主要靠元素标识与属性选择器,不是 Shadow DOM。
- 子组件根节点可受父样式影响,内部后代另看标识。
- deep 放开指定后代部分,外层范围可以继续保留。
- 选择器按真实 DOM 匹配,移动后的节点未必仍在范围内。
- v-html、插槽与全局规则,使用各自适用机制。
- 优先公开样式接口,内部覆盖要记录版本依赖。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →