Vue 单文件 scoped 样式简析

Vue 单文件 scoped 样式简析

💡 原文中文,约1700字,阅读约需4分钟。
📝

内容提要

Vue 的 scoped 样式通过为组件元素添加 data-v 属性并在选择器后附加属性选择器,使样式仅作用于当前组件。子组件元素不带该属性,因此不受影响。若需修改子组件样式,可不用 scoped,或用 /deep/ 使后续选择器不受 data-v 限制,实现样式穿透。

🔎

延伸解读

scoped 的实现机制

Vue 的 scoped 样式通过为当前组件的每个元素添加唯一的 data-v-xxxxx 属性,并在 CSS 选择器后附加对应的属性选择器(如 .money[data-v-9bfd234a]),使样式仅作用于带该属性的元素。子组件内部的元素不会被打上父组件的标识,因此父组件的样式不会影响子组件。若子组件也使用 scoped,则会生成另一个不同的 data-v 属性,两者互不干扰。

样式穿透的两种方式

当需要修改子组件样式时,可以放弃使用 scoped,改为在模板最外层添加一个 id,并将所有样式写在该 id 下,但这依赖 CSS 预处理器。另一种方式是保留 scoped 并使用 /deep/(或 >>>),例如 /deep/ .childclass 会被编译为 [data-v-9bfd234a] .childclass,从而让后续选择器不受 data-v 限制。注意 /deep/ 并非真正“穿透”,而是让 deep 之后的选择器不被加上 data-v 属性。

选择器写法的注意事项

使用 /deep/ 时,如果项目使用了 CSS 预处理器(如 SCSS),可能无法处理 >>> 符号,此时应使用 /deep/。此外,若采用外层 id 包裹的方案,需要确保所有样式都写在该 id 之下,否则仍可能影响全局。这些细节在实际开发中容易忽略,需根据项目环境选择合适的方法。

❓

Q&A

Vue 的 scoped 样式是怎么实现只作用于当前组件的?

Vue 在编译时会为当前组件的所有元素添加一个唯一的 data-v-xxxxx 属性,并在每个 CSS 选择器后附加对应的属性选择器(如 .money[data-v-9bfd234a]),从而将样式限制在带有该属性的元素上。

为什么在父组件中写 scoped 样式不会影响子组件的元素?

因为子组件内部的元素不会被打上父组件的 data-v 属性,父组件的样式选择器都附加了父组件的 data-v 属性选择器,所以无法匹配到子组件元素。

如果子组件也使用了 scoped,父子组件的样式会冲突吗?

不会。每个组件的 scoped 样式都会生成不同的随机 data-v 属性,父子组件的属性选择器不同,因此样式互不干扰。

如何用 /deep/ 穿透 scoped 样式来修改子组件样式?

在 scoped 样式中使用 /deep/ 选择器,例如 /deep/ .childclass { color: red; },编译后 /deep/ 后面的选择器不会被加上 data-v 属性选择器,从而可以匹配到子组件元素。

除了 /deep/,还有哪些方法可以修改子组件的样式?

可以不用 scoped,改为在模板最外层加一个 id,并将所有样式写在该 id 下(通常配合 CSS 预处理器);或者使用 >>>(在纯 CSS 中可用,但某些预处理器无法处理)。

/deep/ 穿透样式的原理是什么?

/deep/ 并不是真的“穿透”了父子组件,而是在 scoped 的前提下做减法:让 /deep/ 后面的选择器不被加上 data-v 属性选择器,从而扩大选择范围。

🏷️

标签

➡️

继续阅读