单页面应用存在的问题

对于浏览器来说,因为只有一个html页面,所以样式是全局起作用的。每个vue页面的样式会作用到所有其他的vue页面。

样式隔离

为了解决单页面应用样式全局起作用的问题,vue引入scoped。通过给vue页面所有元素添加上自定义属性data-v,相当于给当前vue页面的所有元素打上唯一标记。
并且让当前vue页面的样式选择器全变成属性选择器,属性选择器只会选中带有这个属性的,也就是当前页面的样式选中只会选中当前vue页面的元素。

为什么需要样式穿透

想要在父组件修改子组件的样式。比如修改 element-plus 的样式。

怎么实现样式穿透

首先,需要明白两点非常重要的细节:

  1. vue 给所有元素添加唯一标识属性,并不是完全的所有,有些元素不会被加上 data-v
  2. vue 给所有的样式选择器添加属性选择,属性默认只会添加到最后一个选择器上。除非是并集选择器,两个的最后都会被添加。

.app {}实际是.app[data-v-xxx] {}
所以这解释了为什么我们明明设置了这个类给元素,但是样式始终挂不上去。就是因为这个元素没有data-v,而我们的选择器默认是个属性选择器。

实现样式穿透的核心就是,我们可以手动调整属性添加到样式选择器的位置

  • ::v-deep显示表示属性的位置。
  • 现在推荐使用:deep()
    • :deep(.app)=>[data-v-xxx] .app=== :deep() .app=>[data-v-xxx] .app
    • .app :deep(.hhh)=>.app[data-v-xxx] .hhh
    • :deep() {}=>[data-v-xxx] {}
    • .app {}=>.app[data-v-xxx] {}===.app :deep()

注意:less 中使用:deep(选择器)括号里一定要有选择器,不能直接使用 :deep()

我们无法控制 vue 给哪些元素添加自定义属性 data-v,但是我们可以选择怎么调整样式选择器去命中元素。

比如一个小技巧,选中未被 vue 加上 data-v-xxx 自定义属性的元素。
我们可以通过::v-deep:deep()将属性选择提前并加上逗号和后面的类选择器构成并集选择器,这样就让类选择器变成原始模样了。

最佳实践

我们可以通过浏览器查看元素是否有自定义属性,灵活调整样式选择器命中元素。