前言
提起样式穿透,确实很多面试官应该会问到这个,为啥问,因为开发中很多地方会用到,尤其是在你想修改一些像elementui等一些组件库的内部样式的时候。不过很多人往往只停留在了怎么用,遇到样式不生效的时候去各种试,没有再深一步去了解。我希望下次你再和人聊的时候可以说一说样式穿透的使用场景,为什么这样写就可以让样式生效,在组件化开发中scoped是一种防止样式冲突的解决方案,你有没有了解过css module。
发散思维的好处就是你在面临不同的场景的时候可以找到合适的解决方案。
样式穿透
首先,这个概念是建立在你使用scoped的前提下,scoped是为了通过属性选择器,来解决样式冲突的,但是在我们平时开发中比如引入了第三方的组件库的时候,你想直接修改组件库内部的子元素的时候,发现样式不生效了,我们先把场景复现一下: 比如你写了一个elementui的表单元素,并想修改一下该组件内部的一些样式:
<template>
<el-input class="input-diy"></el-input>
</template>
<style scoped lang="scss">
.input-diy{
width:200px;
.el-input__inner{
background-color:red
}
}
</style>
此时,你会发现width宽度是生效的,但是红色的背景不生效,分析不生效的原因:
很明显,编译生成后的代码样式是有属性选择器的,而组件上并没有该属性,属性不能命中,那么你写的样式不生效是理所应当的对吧。
所以引出了样式穿透,来改造一下刚才的代码(vue3的写法):
<template>
<el-input class="input-diy"></el-input>
</template>
<style scoped lang="scss">
.input-diy{
width:200px;
:deep(.el-input__inner){
background-color:red
}
}
</style>
为什么样式生效了,继续分析:
像素眼的你一定发现了之前存在于子元素上的属性跑到了组件的顶层元素(也就是input-diy样式所在的元素)上去了,那么现在内部的样式便可以命中了。下次你再遇到类似的问题应该掌握了分析的思路了。
css module
聊完了样式穿透,可以再说说样式css module,css module同样是来解决样式冲突问题的,其实解决的思路有点类似于BEM(block__element_modifier)方案,在写大型项目的时候不是可能会出现样式冲突的情况吗,那我们就采取暴力的做法,让所有的css命名不重复,css命名全部位于顶级(不嵌套)。只不过BEM的做法是通过命名的规范来实现的不重复。css module采用的是在工程化开发的过程中,利用打包工具的css-loader,style-loader来实现的,原理是什么?通过path结合css生成唯一hash来实现的防止冲突。因为css文件的path是唯一,所以生成的所有的css也是唯一的,最后结合生成的源码css与生成后的hash css的映射应用到项目中,可以让我们在源码的开发过程中做到命名的简化。这段话是对css module的高度总结,如果你对此还是有些陌生,可以用webpack开启css-loader的module模式亲自尝试一下,回来再细细品一下这段话。
尾声
前端框架和技术种类繁杂,并没有银弹可以解决所有场景,但是依然有人前仆后继的去找各种方案来去解决遇到的各种问题,使我们可以站在巨人的肩膀上,看的更高,望的更远。