使用stylelint对CSS/Sass做代码审查

对样式审查?很少人会这么做吧,但实际上开发者应该有这样的态度,尤其是不同团队多人开发时,这一点尤为重要。

在本文中,我将陈述两点:一是为什么我们需要对样式进行审查,二是如何将审查工具融合到整体的构建流程中(适用于 CSS,也适用于 Sass)。

简介

什么是代码审查

代码审查是一个检查代码是否符合编程规范以及查找代码错误的过程,如果要做个比喻,那么它就是编程语言的拼写检查工具。代码审查可以帮助独立开发者更好的维护代码,但它更大的能力是帮助团队维护代码。

为什么我们需要审查样式

对样式进行审查的原因有很多,比如它可以维护代码的一致性,解析代码中的错误,减少冗余代码等等。

下面让我们看几个示例:

.no-space-after-colon {
    display:block;
}           ⬆

.no-semicolon {
    position: relative ⬅
}

代码审查工具可以有效指出上述代码中的不规范之处。在审查工具中规定代码的规范写法虽然不是必要的,但这种做法有助于维护代码的一致性。此外,在团体开发中彼此不熟悉,如果我看到上述的代码会感到很恼火。

.invalid-hex {
    color: #FFF00G;
}                ⬆

代码审查工具也可以指出无效的颜色值,抛出一个类型错误。如果疏漏了这种错误,往往会导致页面上严重的视觉错误。

.unnecessary-prefixes {
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    border-radius: 5px;
}

随着浏览器的升级换代,有一些 CSS3 属性的浏览器前缀已经没有意义了。代码审查工具可以指出这些无意义前缀,此外,将它和 Autoprefixer 搭配起来,可以更加有效。

.duplicate-rule {
    display: block;
    transition: opacity .2s;
    color: #444;
    background-color: #eee;
    transition: background-color .4s; ⬅
}

样式重复是一个常见的错误,就上面的代码而言,这里的 transition 值到底是 opacity 还是 background-color 呢?显而易见, background-color 会替代 opacity 。

代码审查是不是很有用呢?如果这还不够打动你,请继续阅读。

stylelint 简介

stylelint 是一个基于 Javascript 的代码审查工具,它易于扩展,支持最新的 CSS 语法,也理解类似 CSS 的语法。此外,因为它是基于 JavaScript,所以比起 Ruby 开发的 scss-lint 速度更快。

stylelint 是一个强大和现代的 CSS 审查工具,有助于开发者推行统一的代码规范,避免样式错误。

stylelint 由PostCSS 提供技术支持,所以它也可以理解 PostCSS 解析的语法,比如 SCSS。

PostCSS 是一个使用 JS 解析样式的插件集合,它可以用来审查 CSS 代码,也可以增强 CSS 的语法(比如变量和混合宏),还支持未来的 CSS 语法、行内图片等等。

PostCSS 的哲学是专注于处理一件事,并做到极致,目前它已经有了 200 多个插件,由于它们都是基于 JavaScript 编写的,所以运行速度非常快。

PostCSS 和 stylelint 就是我们接下来将要介绍的代码审查工具。

安装

stylelint 的强大之处就在于它非常灵活,无需花费过多的时间过滤各种规则,只需配置需要的规则即可完成 stylelint 的初始化。 stylelint 的配置文档 非常适用于初学者了解相关的审查规则。此外,他们还提供了一份 标准配置文件 用作参考。

在本文中,我将带领大家从一份更友好、简洁的配置开始。就我个人而言,我认为它比官方提供的配置文件更加灵活:

"rules": {
  "block-no-empty": true,
  "color-no-invalid-hex": true,
  "declaration-colon-space-after": "always",
  "declaration-colon-space-before": "never",
  "function-comma-space-after": "always",
  "function-url-quotes": "double",
  "media-feature-colon-space-after": "always",
  "media-feature-colon-space-before": "never",
  "media-feature-name-no-vendor-prefix": true,
  "max-empty-lines": 5,
  "number-leading-zero": "never",
  "number-no-trailing-zeros": true,
  "property-no-vendor-prefix": true,
  "rule-no-duplicate-properties": true,
  "declaration-block-no-single-line": true,
  "rule-trailing-semicolon": "always",
  "selector-list-comma-space-before": "never",
  "selector-list-comma-newline-after": "always",
  "selector-no-id": true,
  "string-quotes": "double",
  "value-no-vendor-prefix": true
}

最后,建议读一下 stylelint 的官方配置文档 ,在其基础上做一些个性化的设置。接下来,让我们将这些审查规则融入到构建流程中。

如何审查 CSS

首先,让我们先来审查 CSS 代码。配置审查工具的过程非常简单,你只需要安装 gulp-postcss / postcss-reporter 和 stylelint 即可:

npm install gulp-postcss postcss-reporter stylelint --save-dev

接下来是 gulp 的配置文件 gulpfile.js :

/**
 * Linting CSS stylesheets with Stylelint
 * http://www.creativenightly.com/2016/02/How-to-lint-your-css-with-stylelint/
 */

var gulp        = require('gulp');

var postcss     = require('gulp-postcss');
var reporter    = require('postcss-reporter');
var stylelint   = require('stylelint');

gulp.task("css-lint", function() {

  // Stylelint config rules
  var stylelintConfig = {
    "rules": {
      "block-no-empty": true,
      "color-no-invalid-hex": true,
      "declaration-colon-space-after": "always",
      "declaration-colon-space-before": "never",
      "function-comma-space-after": "always",
      "function-url-quotes": "double",
      "media-feature-colon-space-after": "always",
      "media-feature-colon-space-before": "never",
      "media-feature-name-no-vendor-prefix": true,
      "max-empty-lines": 5,
      "number-leading-zero": "never",
      "number-no-trailing-zeros": true,
      "property-no-vendor-prefix": true,
      "rule-no-duplicate-properties": true,
      "declaration-block-no-single-line": true,
      "rule-trailing-semicolon": "always",
      "selector-list-comma-space-before": "never",
      "selector-list-comma-newline-after": "always",
      "selector-no-id": true,
      "string-quotes": "double",
      "value-no-vendor-prefix": true
    }
  }

  var processors = [
    stylelint(stylelintConfig),
    // Pretty reporting config
    reporter({
      clearMessages: true,
      throwError: true
    })
  ];

  return gulp.src(
      // Stylesheet source:
      ['app/assets/css/**/*.css',
      // Ignore linting vendor assets:
      // (Useful if you have bower components)
      '!app/assets/css/vendor/**/*.css']
    )
    .pipe(postcss(processors));
});

上面短短五十行代码包含了审查规则和文件路径,请确保资源路径与上面的代码相匹配。

更令人惊奇的是,只需改动一小段代码就可以同时支持 Sass,让我们来修改一下吧。

如何审查 Sass

使用 PostCSS 审查 Sass 代码非常简单,与审查 CSS 代码唯一不同的地方就在于,你需要让 PostCSS 识别 .scss 语法。这一问题可以通过安装 postcss-scss 插件来完成,安装插件后,修改一下配置文件:

npm install postcss-scss --save-dev

修改配置文件:

  //[...]

  return gulp.src(
      ['app/assets/css/**/*.css',
      '!app/assets/css/vendor/**/*.css']
    )
    .pipe(postcss(processors), {syntax: syntax_scss}); ⬅
});

以上就是 gulp 配置文件的全部内容了。总结起来,你需要安装以下工具:

npm install gulp-postcss postcss-reporter stylelint postcss-scss --save-dev
/**
 * Linting Sass stylesheets with Stylelint
 * http://www.creativenightly.com/2016/02/How-to-lint-your-css-with-stylelint/
 */

var gulp        = require('gulp');

var postcss     = require('gulp-postcss');
var reporter    = require('postcss-reporter');
var syntax_scss = require('postcss-scss');
var stylelint   = require('stylelint');

gulp.task("scss-lint", function() {

  // Stylelint config rules
  var stylelintConfig = {
    "rules": {
      "block-no-empty": true,
      "color-no-invalid-hex": true,
      "declaration-colon-space-after": "always",
      "declaration-colon-space-before": "never",
      "function-comma-space-after": "always",
      "function-url-quotes": "double",
      "media-feature-colon-space-after": "always",
      "media-feature-colon-space-before": "never",
      "media-feature-name-no-vendor-prefix": true,
      "max-empty-lines": 5,
      "number-leading-zero": "never",
      "number-no-trailing-zeros": true,
      "property-no-vendor-prefix": true,
      "rule-no-duplicate-properties": true,
      "declaration-block-no-single-line": true,
      "rule-trailing-semicolon": "always",
      "selector-list-comma-space-before": "never",
      "selector-list-comma-newline-after": "always",
      "selector-no-id": true,
      "string-quotes": "double",
      "value-no-vendor-prefix": true
    }
  }

  var processors = [
    stylelint(stylelintConfig),
    reporter({
      clearMessages: true,
      throwError: true
    })
  ];

  return gulp.src(
      ['app/assets/css/**/*.css',
      // Ignore linting vendor assets
      // Useful if you have bower components
      '!app/assets/css/vendor/**/*.css']
    )
    .pipe(postcss(processors), {syntax: syntax_scss});
});

如果你想了解如何使用插件扩展 stylelint,那么就请继续阅读后续内容

扩展 stylelint

和 PostCSS 一样,Stylelint 也可以通过插件进行扩展。接下来让我们通过一些实例学习如何使用审查工具改善代码的可读性和可维护性。

实战演练

话说有个产品经理新接了个 web app 的活,项目正处于紧张的开发中,为了减少一些开发时间,他决定增加一个特性来替代之前的一揽子方案,这个特性是这样的:给组件本身添加一个鼠标悬停时的 box-shadow 效果,同时给组件的内部的链接增加一个鼠标悬停样式。

下面是这个产品经理增加的代码段:

.component {
  position: relative;
  //[...]

  &:hover { ⬅
    box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.75);

    .component__child { ⬅
      ul { ⬅
        li { ⬅
          a { ⬅
            &:hover { ⬅
              text-decoration: underline;
            }
          }
        }
      }
    }
  }
}

真是惨不忍睹!

选择器嵌套是 Sass 最容易被误用的特性,合理使用它可以提高开发效率,滥用则会毁掉整个项目。嵌套往往是由偷懒引起的,这些代码往往难以阅读。上面的 &:hover{...} 的嵌套层级太深,很容易混淆其他开发者对这段代码的理解。最重要的是,这段嵌套在这里完全是没有必要的。

这就是上面的嵌套代码编译生成的 CSS:

.component:hover .component_child ul li a:hover {}
/* What the heck is this?! */

如果后续接手项目的开发者想要覆盖这段级联样式,那真是大工程。所以有一点值得牢记,那就是除非你非常确定嵌套是必要的,否则不要使用它。

幸运的是,我们有插件来阻止这种情况的发生!我们可以安装 stylelint-statement-max-nesting-depth 插件,设置最大嵌套层级来避免过渡嵌套的现象:

npm install stylelint-statement-max-nesting-depth --save-dev

在 gulp 的排位置文件中增加 scss-lint 相关的任务信息:

gulp.task("scss-lint", function() {
  var stylelintConfig = {
    "plugins": [
      "stylelint-statement-max-nesting-depth"
    ],
    "rules": {
      //[...]
      "statement-max-nesting-depth": [3, { countAtRules: false }],
    }
  }

  //[..]
});

为了增强团队的共建意识,我在这里将 limit 设置为了 3 。由于有嵌套层级的限制,所以产品经理就会来自 stylelint 的重构提示。产品经理没怎么细想,就改成了下面这样:

.component:hover {
  box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);

  .component__child {
    ul {
      li {
        a:hover {
          text-decoration: underline;
        }
      }
    }
  }  
}

这个重构之后的版本看起来可读性好多了,但仍然有不少缺陷。事实上,上面代码中的嵌套是完全没有必要的。stylelint 可以检测出这一点,并会强制产品经理重新思考样式代码。

.component:hover {
  box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);
}

.component__child {
  a:hover {
    text-decoration: underline;
  }
}  

现在,代码更加健壮了!stylelint 已经接受了代码,不会再提出异议了。虽然上面的代码没有问题,但我们其实可以做的更好。如果你希望严格要求团队的每一个成员,那么可以禁用嵌套,这会让团队成员,包括产品经理,更加严谨地编写代码。

.component:hover {
  box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);
}

.component__link:hover {
  text-decoration: underline;
}

通过以上介绍和实战演练,希望我已经说服了你:样式审查是非常值得投入的一项流程。代码审查应该成为我们的朋友,只需投入一点时间,就可以收获代码整体的可读性、可维护性。

本文根据 @Scotty Vernon 的《 How to lint your Sass/CSS properly with Stylelint 》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处: http://www.creativenightly.com/2016/02/How-to-lint-your-css-with-stylelint/ 。

posted @ 2016-12-16 10:23  安慕希  阅读(3059)  评论(0编辑  收藏  举报