我们常说,字如其人,而对于一个工程师,代码就是你专业形象的最好表达。养成一个良好的编码风格,至关重要。
而所谓良好的编码风格,实际上就是我们常说的 Readability(可读性),这在我们公司内部是有专门的培训和考核的,作用场景就是日常的CR(Code Review)。这块内容比较多,后面可以单开一个系列来讲,而今天主要还是介绍一些能让你的代码实现更优雅的前端编码小技巧。
选择器的艺术 01
选择器是学习CSS的第一课,属于非常基础的知识,但即便是工作多年的老前端,也不一定能玩通透,依然有很大一部分人对于“信手拈来”的选择器随意使用,不瞻前,不顾后,不做全局考虑,这就会引发一系列问题。
问题1:“这公共样式谁写的?把我样式都覆盖了!”
上述责问你肯定没少听过吧?是的,css选择器的全局性就是容易造成全局环境下的样式覆盖问题,尤其是一些公共样式的书写,如果肆意书写,很可能就会影响到具体页面内的样式。
解决方案:
step1. 基本样式(reset.css) 必须业务非相关,如color值,字体大小等,不允许定义。
step2.
全局样式(g.css)尽可能采用单层类选择器,在不十分确定的情况下,尽量不使用标签选择器。如下图,若是直接对标签p定义了颜色,那很可能会直接影响所有引用该全局样式的页面。
(页面内所有p标签都会变成白色)
step3.
以组件,业务模块等粒度对样式进行抽离封装,并以统一命名规范进行命名,从而变相对样式进行虚拟的命名空间约束,达到“抵消css全局性”的功效,这里推荐采用BEM命名方式,如图:
无法抽离成组件和模块的样式,也可以用类似的方式,在具体页面内以“布局区域”来做粒度,对样式进行约束。
知识更新:
样式覆盖问题当年的确困扰了不少大型项目开发者,最根本的原因还是因为当时业界推崇“关注点分离”,在项目里将html,css,js分别进行充分的隔离和管理,避免所谓的“内联样式”的应用。但随着React和Vue等UI框架的普及,css-in-js的理念深入人心,由于其使用便捷性和天然的模块化管理,消除了开发者对此类问题的恐惧,只需要配合BEM规范,即可相对优雅的管理好项目的样式。
问题2:“这样式权重太大了,不好覆盖啊!” “谁写的important,太坑了吧!”
嗯……看语气就知道了,每次遇到这种问题是很心烦的,css权重问题处理的不好,就会让前端疲于奔命的应付在你覆盖我来我覆盖你的道路上。。。尤其是遇到这种选择器,真的想死:
(截图来自百度糯米首页)
解决方案:
step1.
参考问题1,对css进行命名约束,对于一个组件ui-dialog,全局只能有一个;对于某页面内sec-banner,此页面内也只应该存在唯一一个叫做sec-banner的区域,以此类推。这样,我们就已经把某元素限定在了一个很小的范围内,在此范围内,你就可以肆无忌惮的命名而不用担心命名重复了。
用上图的反例来举吧,这个logo-item其实是位于一个叫“精选品牌”的楼层里,那么我们可以先把这个区域定为“selected-brand”,而这个区域内,logo-item这个类名基本就完全可以做到不冲突,唯一性,那么整个选择器就会是“.selected-brand .logo-item”,当然了,要按我的词汇量,我才不会叫什么logo-item,叫brand不是更好?因为如果叫brand,那这个div里如果还有图片啊,文字啊之类的,就可以采用BEM方式进行约束:brand__img,brand__title,而整体选择器权重就只有两层class,万一有皮肤定制的需求,覆盖起来也是分分钟的事儿对吧。
我们提炼一个方法论:在确保唯一性的前提下,尽可能缩减DOM层级和选择器层级。
step2.
不要轻易使用 !important 和 * 。!important的恶心之处我就不多说了吧,至于*的使用,确实不会增加太多权重,但是!*会让你的样式定义遍布全球每一个角落!这种一棍子打死的选择器使用方式,正常人干不出这种事儿!当然了,存在即合理,并不是说哪儿都不能用,我说的是不要轻易用,除非你很清楚你在干嘛。
问题3:“咦?这样式咋被划掉了?被定义了两遍?”
这次我拿自己的项目开涮,如图:
明显可以看出来,a标签其实已经在全局范围内定义了一个hover颜色,但是在具体业务页面里又定义了一次,当然了,我可以解释其实这里是历史遗留,所以呢?历史遗留问题不应该清理一下?
解决方案:
在写样式的时候,要充分利用样式继承(当然了前提是你能hold住这个样式而不被反覆盖),避免重复定义样式。继承用得好,是可以减少好多工作量的喔~
上面几个问题,只是抛砖引玉,实际生产中,还有很多不优雅的选择器使用方式。
还是那句话,选择器很简单,但是要用好,真的不简单。技术不复杂,但需要融入很多的整体思考,大局观。
一些可以更优雅的场景 02
有很多场景,是我们经常会遇到的,这些场景总会给我们制造一些小麻烦,小困扰,每次我们都会直接针对性的hack掉就完了,实际上,只要我们多思考一点,就可以做得更优雅一些,形成一套可以随时复用的解决方案,就不用每次遇到都hack了。
场景1:子元素margin没有撑开父容器,但影响到相邻元素,从而影响距离计算。
这就是著名的BFC边距折叠问题,具体原理我就不阐述了,各位自行查资料。为什么要把这个问题拿出来说?因为在视觉还原要求较高(像素级还原)的团队,两个元素之间的距离是需要精确计算的。
举例如下图,父容器a其实没有margin,但子元素c的边距使得a与b之间产生了间距,这并不是我们预期的:
我们希望看到的标准输出应该如下图:
解决方案:
原理上就是要解决BFC折叠问题,一般来讲,我们可以用这两种方法解决,
1. 父元素添加overflow:hidden,但前提是该容器内没有需要“溢出容器”的内容,比如小箭头什么的。
2. 给父元素增加伪元素:after,并设置其display:table。其实就是我们平时说的“清除浮动clearfix”,当然原理并不是什么清除浮动,而是在相邻父子元素之间增加一个看不见的间隙,强制性阻止margin折叠。
场景2:<img>标签总会多那么1px空白。
解决方案:
这个经典问题的答案也是网上一搜一大堆,原理是img标签的特殊性(表现为块状元素的行内元素)导致其做垂直对齐时候会默认做基线对齐,而不是底部对齐,从而留出一些空白。
那照这个原理来讲,我们人为让img做底部对齐不就可以了?是的,设置verticle-align:bottom就能解决。
但是我在这里想分享另一个思路,img标签,最好使用一个父容器存放!好处如下:
- 方便添加跳转链接。大部分有图的地方一定会有个链接或者交互动作。
- 方便根据需求随时调整宽高。将图片100%撑满容器,调整容器宽高即可改变图片宽高。
- 方便根据运营需求,随时切换为动态可配置图片。
容器可以做占位,即使图片挂掉,布局也不会错乱,还可以设置默认背景图,优雅容灾。
将图片放入容器后,设置display:block即可解决1px空白问题。更重要的是,我们不仅仅解决了1个问题,还拥有了一套更加优雅的,可以应对各种复杂问题的图片解决方案,岂不更好?
场景3:弹窗垂直居中(泛指需要居中在整个视窗中间的绝对定位元素)
解决方案:
移动端强烈推荐使用translate(-50%,-50%)搞定,方便简单无副作用。
PC端就比较蛋疼一点,若是元素宽高固定,那传统做法使用负值margin就可以搞定,但宽高不固定呢?
给大家推荐一个非常好用的方法,给遮罩层加一个:afrer并设置其高度100%撑满容器,但由于其宽度为0所以其实不占位,然后将需要垂直居中的元素都设置为dislpay:inline-block从而将问题转换为行内元素如何垂直居中问题,如图:
结语 03
这次给大家分享的意义,其实不在于说你能学到几个解决实际问题的方法和技巧,更重要的是学到一种思维方式,即培养一种全局思维,跳出执行层面的局限性,更多的从整体收益来看待问题。
代码行中悟真知,优雅的解决问题,会让你的思维高度提升一大个层级!