原文地址: https://medium.com/free-code-camp/how-to-get-better-at-writing-css-a1732c32a72f
原文作者: Thomas Lombart
翻译作者: hanxiansen
中文标题:如何更优雅的编写CSS代码
直白的说:编写优秀的 css 代码可能是很痛苦的。很多程序员都不想从事 CSS 开发—我可以做任何事情,除了css以外。
当我在编写app时,css是我最不喜欢的部分,但你又不能逃避它,对吗?我的意思是,在专注于用户体验和设计上,我们不能跳过css这一部分。
当开始一个项目是,一切都很好。你有几个css选择器:.title input #app
, 很简单。
但是,当你的app变得越来越大时,它开始变得糟糕起来。你会对css的选择器感到困惑,你发现自己把类似 div#app .list li.item a
的css代码编写一遍又一遍,你把所有的css代码放在文件末尾,因为你根本不在乎糟糕的css代码,因为:500行css代码完全无法维护。我今天的目的是:让你更好的编写css代码。我想让你看看你以前的项目代码,然后想:哦,天哪,我写了些神马玩意儿啊。
好吧,你可能会想,你说得有道理,但不是有css框架吗?是的,这就是框架所表达的意思—让我编写更好的css代码。
当然,这些框架也有一些缺点:
- 它经常导致平庸的设计
- 定制或超越css框架会很困难
- 在使用它们之前,你必须先学习它们
毕竟,你看这篇文章是带着目的的,对吧,所以不要在纠结框架不框架了,让我们学习如何在原生css方面让它变得更好吧,
Ps: 这不是一篇关于如何设计漂亮app的文章,它是关于编写可维护和可组织的css代码的学习文章
SCSS
在本文的示例代码中我将使用SCSS编写。
SCSS是css的预处理器。基本上,它是CSS的超集:它添加了一些很酷的特性,比如:变量、嵌套、导入和混合。我会略将下我们马上要使用的特性。
变量
在scss中你可以使用变量。主要好处:可重用性。让我们假设你的app中有一个颜色调色板。你的主题色是蓝色。所以你到处都要使用该颜色:按钮背景色、标题颜色、链接颜色,到处都是蓝色。
突然,你不喜欢蓝色了,你喜欢上绿色了:
- 没使用变量情况下:改变每行使用了蓝色的css代码
- 使用变量情况下:只需要改变颜色变量:)
// Declare a variable
$primary-color: #0099ff;// References a variable
h1 {
color: $primary-color;
}
嵌套
你也可以使用scss进行代码嵌套:
代码语言:javascript复制h1 {
font-size: 5rem;
color: blue;
}h1 span {
color: green;
}
变为:
代码语言:javascript复制h1 {
font-size: 5rem;
color: blue;
span {
color: green;
}
}
可读性是不是变得更强?使用嵌套可以使你花费更少的时间来编写复杂的css选择器。
分块和导入
当涉及到可维护性和可读性上时,不可能将所有的代码都保存在一个大文件中。在实验性或小的APP中,这么做可以满足需求,但在专业级别的app上。想都别想。幸运的是,SCSS允许我们进行专业的app编写。
你可以通过使用前置下划线命名的文件来创建分块文件:_animations.scss、_variables.scss等。至于导入,我们使用 @import 指令。例如,你可以进行如下操作:
代码语言:javascript复制// _animations.scss
@keyframes appear {
0% {
opacity: 0;
} 100% {
opacity: 1;
}
}
// header.scss
@import "animations";
h1 {
animation: appear 0.5s ease-out;
}
呃,你可能会想,你在这里犯了个错误!它是_animations.scss
,而不是animations
。
非也,scss足够聪明,当你以这种方式进行命名时,它可以知道你想指代的是分块文件。
这就是我们需要知道的关于变量、嵌套、分块和导入所有的新星。scss还有一些更多的特征,比如混合、继承和其它指令(@for,@if,…)。但我不会在这里谈它们。
如果你想了解更多关于scss的知识,可以阅读它们的文档,这更便于理解和学习。
CSS 代码组织方案:BEM
我曾经无数次给我的css类名提供我能想到的全部术语,你懂的,比如这些命名:.button .page-1 .page-2
。我经常不知道如何进行命名。然而这又很重要,如果你正在编写一个app项目,出于某些原因,你决定搁置这个项目几个月,或者更糟,有人要收回该项目,如果你的css代码没有正确的命名,你很难知道你到底写了个啥。
BEM 帮助我们解决该问题。BEM 是一种命名约定,表示“块 元素 修饰符”。
该方案可以使我们的代码更加结构化,更加模块化和更大的可复用性。现在我来解释下什么是块、元素和修饰符。
块
块通常被视为一个组件。还记得小时候玩的乐高吗?好的,让我们回到小时候。
你打算如何建造一座简单的房子?你需要一个窗户,一个屋顶,一扇门,一些墙,就这些东西,这些就是我们需要的块。这些块都是有命名意义的。
命名:块名称:.block
示例: .card
, .form
, .post
,.user-Navigation
。
元素
现在你要怎样用你的乐高积木来建造一个窗户呢?嗯,其中某些看起来像架子,当你组装好四个框子时,你会得到
一个漂亮的窗户。这就是我们的元素。它们是“块”的一部分,它们是建造“块“的必需品,但离开了”块“,它们什么都不是。
命名:块名称 __ 元素名称: block__element
示例:.post__author
,.post__date
,.post__text
修饰符
现在你已经建造好了窗户,你可能需要一个绿色的窗口或者小点的窗户。这些就是所谓的修饰符。它们是块或元素的标识,用于更改行为、外观等。
命名: 块元素名称或元素名称 --
修饰符名称: .Block__element-modifier
,.Block-modifier
。
示例:.post--important
,.post__btn--disabled
注意点
- 当你使用 BEM 时,你的命名只有 class 类名并且只使用 class 类名,没有 id ,没有标签,就只使用 class 类名。
- 块/元素可以嵌套到其它块/元素中,但它们必须是完全独立的。记住这个词:独立。所以,不要在按钮元素上写margin,因为你想要把按钮放在标题元素下,否则你的按钮将会和标题元素强耦合。这种情况请使用具体的 class 类名来替代。
- 是的,你的 HTML 文件将会因为 BEM 变得臃肿,但比起BEM带来的好处,这只是一个无足轻重的小缺点。
举个例子
这是给你的练习。你浏览那些你常逛的网址,试着分析哪些是块,哪些是元素,那些是修饰符。
例如,我在谷歌商店中分析到的是这样的:
轮到你了,保持好奇心,想想怎样可以变得更好。和往常一样,你必须自己搜索,实验,创造。这样你才能完成你的分析。
把这些知识进行结合
在下例中你会发现 BEM 的强大之处:
编写单个文章组件示例 —— https://codepen.io/thomlom/pen/RJvVdQ
编写多个按钮示例——https://codepen.io/thomlom/pen/VdgzmJ
CSS 文件组织方案:7-1模式
还在跟着我一起学习吗?真棒!现在让我们看看如何组织 css 文件。这部分将真正的帮助你提高工作效率,并允许你立即能找到需要修改的 css 代码位置。
为了做到这点,我们将学习 7-1模式。
这玩意儿简单不,你可能会想。
相信我,该模式非常简单,你只需记住如下两条原则即可:
- 所有的分块放在7个不同的文件夹中
- 把这些分块通过 import 引入到一个
main.scss
文件中,该文件放到根目录,嗯,就是这么简单。
7个文件夹:
- base: 该文件中,放置所有的样板代码。我这里说的样板文件,是指每次你开始一个新项目时,你要写的所有 CSS 代码。例如:排版规则,动画特效,公共工具(这里的公共工具是指如
margin-right-large, text-center,
..)等等。 - components: 该命名已经指明了其地位。此文件包含用于构建页面所需的组件,如:buttons、forms、swipers、popups等等。
- layout: 用于布局页面的不同部分。即:header、footer、navigation、section、grid等。
- pages: 有时候你可能写了一个页面,但要为其制定专属的样式,所以你把这种专属样式放置在 pages 文件夹中。
- themes: 如果你的 app 需要拥有不同的主题(黑暗主题,默认主题等等) ,把这些主题放置在该文件夹中。
- abstracts: 把你的所有函数,连同变量和mixins一起放置在这里面,简言之,就是放置所有的助手。
- vendors: 有什么 app 或项目不依赖于外部库吗?将那些不依赖与你自己写的样式文件防治站该文件夹中。你可能想在这里面添加 Font Awesome 文件、Bootstrap 等等类似文件。
main 文件
在该文件中,引入你的所有区块文件
代码语言:javascript复制@import abstracts/variables;
@import abstracts/functions;
@import base/reset;
@import base/typography;
@import base/utilities;
@import components/button;
@import components/form;
@import components/user-navigation;
@import layout/header;
@import layout/footer;
...
是的,这一切看起来很厉害,但你可能会想,这种架构适合大型项目,但不适用于小项目。所以,这里还有一个适合较小项目的版本。
首先,您不需要 vendors 文件夹。你只需将所有的外部 css 依赖放到头部的链接标签中。然后,你可以跳过主题文件夹,因为你的 app 可能只有一个主题。最后,你的页面也不会有很多特定样式,所以你也可以跳过那个文件夹。太好了,只剩4个文件夹了!
然后,你还面临两个选择:
- 你希望你的 css 代码是有组织的并遵循7-1模式,因此你保留了abstract、components、layout和base文件夹。
- 你想把所有的文件包括
main.scss
文件都放置在一个大文件夹中,类似如下: sass/ _animations.scss _base.scss _buttons.scss _header.scss ... _variables.scss main.scss 这取决于你自己。 ok,你成功说服我采用你的方案,但有时候浏览器不支持scss文件,咋整? 说得好!最后一步,我们将学习如何立即将 scss 编译为 css。 SCSS 到 CSS 为了做到这一步,网我们需要 Node.js 和 NPM(或者Yarn) 我们将使用一个名为node-sass
的包,它允许我们将.scss
文件编译为.css
文件。 其 CLI命令行界面非常易用: node-sass <input> <output> [options] 该命令行还有很多参数项,但这里我们只使用两个: 如果你是个好奇者(我希望你是,开发者应该是一个好奇者),自己去这里看相关资料吧 现在我们知道如何使用工具了,剩下的就好办了,步骤如下:touch index.html
mkdir -p sass/{abstracts,base,components,layout} css
cd sass && touch main.scss
{ ... "scripts": { "watch": "node-sass sass/main.scss css/style.css -w", "build": "node-sass sass/main.scss css/style.css --output-style compressed" }, ... } <!DOCTYPE html> <html lang=”en”> <head> <meta charset=”UTF-8"> <meta name=”viewport” content=”width=device-width, initial-scale=1.0"> <meta http-equiv=”X-UA-Compatible” content=”ie=edge”> <link rel=”stylesheet” href=”css/style.css”> <title>My app</title> </head> <body> <h1 class=”heading”>My app</h1> </body> </html> 就这样,你准备出发了!在你进行编码是运行npm run watch
,并在浏览器中打开index.html
文件,如果你想压缩你的 css 文件,使用npm run build
命令- 在
index.html
的head
标签中将编译好的 css 文件进行引入 - 在
package.json
文件中添加这些script
- 生成项目:
mkdir my-app && cd my-app
- 初始化项目:
npm init
- 添加
node-sass
依赖库:npm install node-sass --save-dev
- 创建你的文件夹,你的
index.html
和main.scs
文件 -w
: 监听目录和文件。这意味着node-sass
将会监听你代码的任何更改,当他们发生改变时,它会自动编译为css,这在开发中是个很有用的功能。--output-style
: 指定编译出的 css 文件存放位置,它可以是以下值之一:nested|expanded|compact|compressed
,我们将使用它来构建你的 css 文件。
- 在
添加实时重载
你可能亟需添加实时重载功能以提高开发效率,而不是手动重新加载本地index.html
文件。
步骤如下:
- 安装
live-server
依赖:npm install -g live-server
。注意:这是个全局package - 添加
npm-run-all
依赖:npm install npm-run-all
:它将允许我们同时运行多个script - 在
package.json
文件中添加如下script
{
...
"scripts": {
"start": "npm-run-all --parallel liveserver watch",
"liveserver": "live-server",
"watch": "node-sass sass/main.scss css/style.css -w",
},
...
}
现在,当你运行npm run start
,你可以立即看到你的更改,而无需触及任何内容。
添加 autoprefixer
设定好了开发工具,现在我们来讨论下构建工具,特别是[Autoprefixer]( Autoprefixer)。
它是一个工具,可以解析 CSS 并使用 can I use 中的值将浏览器供应商前缀添加到 css 规则中。
实际上,在构建网站时,你可能会使用到并非所有浏览器都完全支持的新特性,这时候,添加浏览器供应商前缀可以支持这些新特性,下面是示例:
代码语言:javascript复制-webkit-animation-name: myAnimation;
-moz-animation-name: myAnimation;
-ms-animation-name: myAnimation;
这些特性写起来很乏味。因此我们需要 autoprefixer 来使我们的 css 代码能与浏览器兼容,而不会带来额外的复杂度。
所有我们按如下方式编写 css 代码:
- 将所有的 scss 文件写入一个主文件中
- 通过 Autoprefixer为css添加浏览器供应商前缀
- 编译 css 文件
这将是最后的步骤了,所有耐心和我一起完成吧:
- 添加两个依赖:
postcss-cli
和autoprefixer
:npm install autoprefixer postcss-cli --save-dev
- 修改
build
scirpt,添加两个script到package.json文件中:
{
...
"scripts": {
"start": "npm-run-all --parallel liveserver watch",
"liveserver": "live-server",
"watch": "node-sass sass/main.scss css/style.css -w",
"compile": "node-sass sass/main.scss css/style.css",
"prefix": "postcss css/style.css --use autoprefixer -o css/style.css",
"compress": "node-sass css/style.css css/style.css --output-style compressed",
"build": "npm-run-all compile prefix compress"
...
}
现在,你运行npm run build
时,你的 css 代码将被压缩,并且已经添加了浏览器供应商前缀名,太棒了,不是吗?