scoped样式及:deep()深度选择

67 阅读3分钟

在 Vue 单文件组件中,为防止样式混乱,常常当在 style 标签中添加 scoped 属性,让它的 CSS 只影响当前组件的元素。

但是我相信大家在开发中,尤其是使用组件库时,常常有这样的体验——样式不生效。大部分时候加个 :deep() 就能解决,但是有些时候这样也解决不了,于是我们可能会再加一个没有 scoped 属性的 style 标签,把样式写到全局来让样式生效,但是这样可能会造成样式污染,必须保证类名全局唯一。

接下来我们就来讨论一下 scoped 属性是怎么实现样式隔离的,以便在设置样式时有的放矢,准确选中目标元素。

scoped样式隔离

scoped 实现样式隔离实际上是通过给元素加上一个唯一标识属性(data-v-xxxx)来实现的。每个单文件组件都有一个全局唯一的标识属性,该组件内部的所有元素都有这个相同的属性。

下图中 .chat-container 是一个单文件组件的根元素,这个组件的唯一标识属性值为 data-v-39a5154,可以看到这个组件中的元素都有这个相同的标识属性。

在控制台查看元素,如下图,可以看到编译时为每个 CSS 选择器都加上了该属性的选择器,只有拥有 data-v-39a51454 属性的 main-container 元素才会被这个选择器选中,样式才会生效。

注意: 对于引入的子组件,只有子组件的根元素会添加父组件的唯一标识属性,根元素内的子元素都只有子组件的标识属性,这通常也是使用组件库设置样式不生效的原因。同样是上面的文件,我们把它展开查看。

这里我用红框框出的是子组件自身的标识属性,而绿框框出的则是父组件的标识属性,可以看到子组件只有根元素添加了父组件的标识属性。

:deep()深度选择

我们前面说了子组件只有根元素会添加父组件的标识属性,也就是说,使用 scoped 后,父组件的样式将不会渗透到子组件中。处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,需要使用 :deep() 这个伪类。

:deep() 深度选择器会把属性选择器移动到父元素上。例如我们如果在 .chat-container 组件中使用 .conversation-container 这个类选择器写了样式,这个样式将不会生效,因为这个选择器会被编译成为 .conversation-container[data-v-39a51454],而这个元素实际上并没有这个属性,因此不会被选中。

这种情况下我们就可以加上深度选择器,写成 :deep( .conversation-container ),这样选择器会被编译成为 [data-v-39a51454] .conversation-container,即只要 .conversation-container 在 DOM 结构中是 “带 data-v-39a51454属性 的组件元素” 的后代,它就会被该选择器匹配到,从而它的样式就能生效。