如何更优雅的编写 CSS 代码【译】

1,391 阅读12分钟

原文地址: medium.com/free-code-c…

直白的说:编写优秀的 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进行代码嵌套:

h1 {
  font-size: 5rem;
  color: blue;
}h1 span {
  color: green;
}

变为:

h1 {
  font-size: 5rem;
  color: blue;
  
  span {
    color: green;
  }
}

可读性是不是变得更强?使用嵌套可以使你花费更少的时间来编写复杂的css选择器。

分块和导入

当涉及到可维护性和可读性上时,不可能将所有的代码都保存在一个大文件中。在实验性或小的APP中,这么做可以满足需求,但在专业级别的app上。想都别想。幸运的是,SCSS允许我们进行专业的app编写。

你可以通过使用前置下划线命名的文件来创建分块文件:_animations.scss、_variables.scss等。至于导入,我们使用 @import 指令。例如,你可以进行如下操作:

// _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 的强大之处:

编写单个文章组件示例 —— codepen.io/thomlom/pen…

编写多个按钮示例——codepen.io/thomlom/pen…

CSS 文件组织方案:7-1模式

还在跟着我一起学习吗?真棒!现在让我们看看如何组织 css 文件。这部分将真正的帮助你提高工作效率,并允许你立即能找到需要修改的 css 代码位置。

为了做到这点,我们将学习 7-1模式

这玩意儿简单不,你可能会想。

相信我,该模式非常简单,你只需记住如下两条原则即可:

  1. 所有的分块放在7个不同的文件夹中
  2. 把这些分块通过 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 文件

在该文件中,引入你的所有区块文件

@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个文件夹了!

然后,你还面临两个选择:

  1. 你希望你的 css 代码是有组织的并遵循7-1模式,因此你保留了abstract、components、layout和base文件夹。

  2. 你想把所有的文件包括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]
    

    该命令行还有很多参数项,但这里我们只使用两个:

    • -w: 监听目录和文件。这意味着 node-sass将会监听你代码的任何更改,当他们发生改变时,它会自动编译为css,这在开发中是个很有用的功能。
    • --output-style: 指定编译出的 css 文件存放位置,它可以是以下值之一:nested|expanded|compact|compressed,我们将使用它来构建你的 css 文件。

    如果你是个好奇者(我希望你是,开发者应该是一个好奇者),自己去这里看相关资料吧

    现在我们知道如何使用工具了,剩下的就好办了,步骤如下:

    • 生成项目:mkdir my-app && cd my-app
    • 初始化项目:npm init
    • 添加node-sass依赖库:npm install node-sass --save-dev
    • 创建你的文件夹,你的index.htmlmain.scs文件
    touch index.html
    mkdir -p sass/{abstracts,base,components,layout} css
    cd sass && touch main.scss
    
    • package.json文件中添加这些script
    {
      ...
      "scripts": {
        "watch": "node-sass sass/main.scss css/style.css -w",
        "build": "node-sass sass/main.scss css/style.css --output-style compressed"
      },
      ...
    }
    
    • index.htmlhead标签中将编译好的 css 文件进行引入
    <!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文件。

步骤如下:

  • 安装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 规则中。

实际上,在构建网站时,你可能会使用到并非所有浏览器都完全支持的新特性,这时候,添加浏览器供应商前缀可以支持这些新特性,下面是示例:

-webkit-animation-name: myAnimation;
-moz-animation-name: myAnimation; 
-ms-animation-name: myAnimation;

这些特性写起来很乏味。因此我们需要 autoprefixer 来使我们的 css 代码能与浏览器兼容,而不会带来额外的复杂度。

所有我们按如下方式编写 css 代码:

  • 将所有的 scss 文件写入一个主文件中
  • 通过 Autoprefixer为css添加浏览器供应商前缀
  • 编译 css 文件

这将是最后的步骤了,所有耐心和我一起完成吧:

  • 添加两个依赖:postcss-cliautoprefixernpm 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 代码将被压缩,并且已经添加了浏览器供应商前缀名,太棒了,不是吗?