单页面应用存在的问题
对于浏览器来说,因为只有一个html页面,所以样式是全局起作用的。每个vue页面的样式会作用到所有其他的vue页面。
样式隔离
为了解决单页面应用样式全局起作用的问题,vue引入scoped。通过给vue页面所有元素添加上自定义属性data-v,相当于给当前vue页面的所有元素打上唯一标记。
并且让当前vue页面的样式选择器全变成属性选择器,属性选择器只会选中带有这个属性的,也就是当前页面的样式选中只会选中当前vue页面的元素。
为什么需要样式穿透
想要在父组件修改子组件的样式。比如修改 element-plus 的样式。
怎么实现样式穿透
首先,需要明白两点非常重要的细节:
- vue 给所有元素添加唯一标识属性,并不是完全的所有,有些元素不会被加上 data-v
- 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()
将属性选择提前并加上逗号和后面的类选择器构成并集选择器,这样就让类选择器变成原始模样了。
最佳实践
我们可以通过浏览器查看元素是否有自定义属性,灵活调整样式选择器命中元素。