如何编写高质量CSS
很多前端同行在潜意识里都觉得 js 比 css 更加重要,所以平时都不是很注重 css 编写的质量。然而,虽然页面的交互能力很大程度取决于 js,但是 css 代码的质量却直接决定了页面的美观程度。所以,css 编写地好与坏,直接决定了用户使用产品的第一印象。因此,编写高质量的 css 代码,是一个优秀前端必须掌握的技能。
为什么要编写高质量的CSS
- 超高质量还原设计稿(专业性)。
- 便于阅读和维护(可读性和维护性)。
- 可扩展和样式复用(扩展性和复用性)。
预期效果
- 开发的页面得到用户、产品、老板的认可。
- 让其他人阅读和维护 css 代码时,很容易看得懂,甚至是惊叹 css 可以如此写,而不是吐槽。
- 抽离项目和组件相同的 css 样式。如此,在修改和扩展样式时只需修改公共 css 代码,就可以保证样式统一。
编写原则
- 编写尽量遵循团队规范,保持团队整体风格,避免强个人主义。
- 使用较合理、较主流的 css 属性。
- 编写时,尽量使用较少的 css 属性,避免重复写已继承的属性、删掉无效属性和多余属性。
- 计算交给浏览器去做,保持布局的自适应性和健壮性。
- 必须考虑 css 属性的兼容性与产品支持浏览器的版本。
- 避免使用耗性能的属性。
- 如果 css 可以做到,就不要使用 js。
最佳实践
命名
BEM
BEM 命名是目前推荐的比较主流的命名规范。
格式:block__element--modifier
优势:
- 高可用的,强大的,而且简单的命名规范。
- 使前端代码更加易读和理解,容易与他人协作,容易扩展,更加强壮和明确,关键是更加严谨。
- 组件之间的完全解耦,不会造成命名空间的污染。
- 不需要过多考虑class如何起名。
虽然 BEM 命名会使得 class 类名变长,但经过 GZIP 等压缩后,文件的体积其实并无太大影响。
规则:
开发中则推荐按照
[命名空间]-[组件名/块]__[元素名/元素]--[修饰符]的格式命名。block
- 块名需能清晰的表达出,其用途、功能或意义,具有唯一性。
- 每个块在逻辑上和功能上都相互独立。
- 由于块是独立的,可以在应用开发中进行复用,从而降低代码重复并提高开发效率。
- 块可以放置在页面上的任何位置,也可以互相嵌套。
element
- 元素名需能简单的描述出,其结构、布局或意义,并且在语义上与块相关联。
- 不能与块分开单独使用。
- 块的内部元素,都被认为是块的子元素。
- 一个块中元素的类名必须用父级块的名称作为前缀,因此不能写成:
block__elem1__elem2。
modifier
- 修饰符需能直观易懂表达出,其外观、状态或行为。
- 参考值:disabled、active、selected、checked、highlight、fixed、size-big、size-small、color-yellow、bg-blue等等。
- 在必要时可进行扩展,书写成:
block__elem_modifier_modifier,第一个modifier表示其命名空间。
注意:
- BEM 命名原则上嵌套不会超过两层。
示例:
demo
<!-- good -->
<!-- dark 表明 search 组件的外观 -->
<form class="main-search main-search-form--dark">
<input class="main-search__input">
<!-- disabled 表明 search__button 的状态 -->
<button class="main-search__button main-search__button--disabled">Search</button>
</form>语义不符
/* bad */
.app__content--left .color-gray {}
/* good */
.app-content__left--color-gray {}布局
推荐使用 flex 布局,斟酌使用 grid 布局(需要考虑兼容性),避免使用 float + position 布局。
左定宽,右自适应两栏布局
示例:
flex 实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>左定宽,右自适应两栏布局 - flex</title>
<style>
.two-cols-layout {
display: flex;
}
.two-cols-layout__left {
width: 300px;
height: 600px;
background: #D24D57;
}
.two-cols-layout__right {
flex: 1;
height: 600px;
background: #2C3E50;
}
</style>
</head>
<body>
<div class="two-cols-layout">
<div class="two-cols-layout__left"></div>
<div class="two-cols-layout__right"></div>
</div>
</body>
</html>grid 实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>左定宽,右自适应两栏布局 - grid</title>
<style>
.two-cols-layout {
display: grid;
grid-template-columns: 300px auto;
grid-template-rows: 600px;
}
.two-cols-layout__left {
background-color: #D24D57;
}
.two-cols-layout__right {
background-color: #2C3E50;
}
</style>
</head>
<body>
<div class="two-cols-layout">
<div class="two-cols-layout__left"></div>
<div class="two-cols-layout__right"></div>
</div>
</body>
</html>三栏两列自适应布局
示例:
flex 实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>三栏两列自适应 - flex</title>
<style>
.three-cols-layout {
display: flex;
}
.three-cols-layout__left {
flex: 1;
height: 300px;
background: #D0D0D0;
}
.three-cols-layout__middle {
flex: 1.5;
height: 300px;
background: #D24D57;
}
.three-cols-layout__right {
flex: 2;
height: 300px;
background: #2C3E50;
}
.mt {
margin-top: 8px;
}
</style>
</head>
<body>
<div class="three-cols-layout">
<div class="three-cols-layout__left"></div>
<div class="three-cols-layout__middle"></div>
<div class="three-cols-layout__right"></div>
</div>
<div class="three-cols-layout mt">
<div class="three-cols-layout__left"></div>
<div class="three-cols-layout__middle"></div>
<div class="three-cols-layout__right"></div>
</div>
</body>
</html>grid 实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>三栏两列自适应 - grid</title>
<style>
.three-cols-layout {
display: grid;
grid-template-columns: 1fr 1.5fr 2fr; /* 三栏宽度 */
grid-template-rows: 300px 300px; /* 两列高度 */
grid-gap: 8px 0; /* 间隔 */
}
.three-cols-layout__item--bg-gray {
background-color: #D0D0D0;
}
.three-cols-layout__item--bg-red {
background-color: #D24D57;
}
.three-cols-layout__item--bg-black {
background-color: #2C3E50;
}
</style>
</head>
<body>
<div class="three-cols-layout">
<div class="three-cols-layout__item--bg-gray"></div>
<div class="three-cols-layout__item--bg-red"></div>
<div class="three-cols-layout__item--bg-black"></div>
<div class="three-cols-layout__item--bg-gray"></div>
<div class="three-cols-layout__item--bg-red"></div>
<div class="three-cols-layout__item--bg-black"></div>
</div>
</body>
</html>宽度
宽度分离原则
引自《css世界》,若有疑问,请阅之。
所谓 宽度分离原则 ,就是 css 中的 width 属性不与影响宽度的 padding/border(有时候包括 margin)属性共存,也就是不能出现以下的组合:
.box { width: 100px; border: 1px solid; } 或者 .box { width: 100px; padding: 20px; }
推荐写法:
.father {
width: 180px;
}
.son {
margin: 0 20px;
padding: 20px;
border: 1px solid;
}挑战宽度分离的写法:
如此,以 css box-sizing 属性节约一个html元素,但是在 margin 的场景有局限,因为 box-sizing: border-box 的作用不计算 margin。
.father {
box-sizing: border-box; /* 通过 box-sizing 改变 width 作用的细节 */
width: 180px;
margin: 0 20px;
padding: 20px;
border: 1px solid;
}垂直居中
文字图标垂直居中
good - 使用 vertical-align 属性
优点是文字或图标的 font-size 发生变化后,依然是垂直居中的。
<!-- good -->
<style>
.middle {
vertical-align: middle;
}
</style>
<div>
<span class="middle">上传图标</span>
<i class="el-icon-upload middle"></i>
</div>bad - 使用绝对定位 计算 top
如此,当行高变化或图标的 font-size 发生变化后,则需要重新计算和修改 top,这种计算应该由浏览器完成,保证布局的健壮性。
<!-- bad -->
<style>
.el-icon-upload {
position: absolute;
top: 4px;
}
</style>
<div>
<span>上传图标</span>
<i class="el-icon-upload"></i>
</div>典型错误
块级元素设置 width: 100%
项目很多 el-table 都会设置自定义样式 width: 100%,官方文档 demo 也这么写,但却是多余的。
滥用 important
当属性优先级不够时,可以考虑通过其他方式提高优先级,而不是无脑设置 !important,这样会导致样式很难维护。
滥用 calc
当 width 与 padding 等属性同时存在时,为了精确控制盒子宽度,会出现类似 width: calc(300px - 15px) 的写法。这种问题可以通过 宽度分离原则 或者 box-sizing: border-box 属性避免。
overflow-x: hidde 和 overflow-y: auto 同时存在
当 overflow-x: hidde 时, overflow-y 默认为 auto,则 overflow-y: auto 可以省略。
使用 scss 嵌套的层级很深
如果出现嵌套层级很深的情况,可以考虑布局能否优化,命名是否规范或者能否改成 BEM 命名。
/* bad */
.box {
.left {
.top {}
}
.right {
.nav {}
.content {
.header {}
.middle {}
.footer {}
}
}
}/* good */
.box-left__top {}
.box-right__nav {}
.box-right-content__header {}
.box-right-content__middle {}
.box-right-content__footer {}最后,重视CSS
相比js或者其他编程语言,css 无疑是简单的,因为 css 鲜有逻辑,无须算法,只是规范。何为规范,规范是被大家认可和遵循的标准。对于 css,规范就是所有 css 属性和特性的集合。在 css 的世界里,页面上的任何看似简单的呈现都是由许许多多属性共同作用的结果。
但是这个世上,越是简单的东西却是越难深入,就好比1+1为何等于2?
所以,要完全深入理解 css 很难,因此我们需要基于现有的 css 制定符合团队的规则和规范,由大家一起编写维护,一起遵循推广。让我们产出的页面更美观,风格较统一,保证 css 代码的可读性、可维护性、扩展性以及复用性。同时让页面得到用户、产品以及老板的认可,体现团队的优势和能力。
参考
网站
书籍
《CSS世界》 - 张鑫旭