MinweiShen

怎样正确使用Stylelint来对你的Sass/CSS代码静态检查

原文链接: www.creativenightly.com

2016年2月12日

最后更新于 2016年2月27日

很少人会去对样式表进行静态检查。不过,许多人应该尝试这么做,尤其是在一个大家都会修改代码库的队伍里。

在这篇文章里,我会讲一讲为什么我们需要对样式表进行静态检查,以及_怎样_把纯CSS或者Sass的样式表静态检查加入我们的构建流程中。

目录:

  1. 简介

  2. 什么是静态检查?

  3. 我们为什么要对样式表静态检查?

  4. Stylelint简介

  5. 配置

  6. 怎么检查CSS

  7. 怎么检查Sass

  8. 用插件扩展Stylelint

  9. 例子: 实际使用静态检查

  10. 后记

  11. 一键安装Sass的gulp构建流


简介

什么是静态检查?

静态检查就是对源代码进行语法和代码规范的检查。它对定位一些编程过程中常见或不常见的错误都很有帮助。本质上,它就是一个编程语言的拼写检查工具。静态检查不仅仅在你一个人工作时很有帮助,在很多(不小心的)人会接触代码的团队里,静态检查更是会带来额外的好处。

静态检查器是一个实现静态检查(检查代码质量)的程序或者工具。目前,主流的语言,如C,Python,JavaScirpt,CSS等都有对应的静态检查工具。

我们为什么要对样式表静态检查?

对样式表进行静态检查的原因有很多。它能确保代码的一致性,定位代码中的错误,减少冗余代码,以及避免出现偷懒的写代码方式。

让我们来看一些例子

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

.no-semicolon {
    position: relative ⬅
} 

静态检查很擅长发现这样的编码风格问题。怎么样配置你的代码风格检查不是最重要的,重要的是它们保证了代码的一致性。而且,我不知道你怎么想,但是对我来说,这样的两种编码风格问题是我不能忍受的。

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

静态检查同样很擅长发现像是由于拼写错误导致的不合法的hex颜色值。这样的错误如果没被发现,可能会破坏你们非常重要的显示效果。

.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; ⬅
} 

重复的规则是一种常见的错误。如果程序员希望不透明度跟背景色都是动态过渡的,那上面的代码里,不透明的过渡就不会起作用。不过,静态检查可以发现这个错误。

信了么?如果还没有,那就接着往下看...

Stylelint简介

Stylelint是一个JavaScirpt实现的可扩展、灵活的CSS静态检查工具。它在CSS静态检查方面是最新、最好的。它支持最新的CSS语法,理解类CSS语法,并且支持插件扩展。还有,由于它是用JavaScirpt而不是Ruby实现的,它比scss-lint要快得多得多。

Stylelint是一个高效、现代的CSS静态检查工具,它能帮助你实现代码规范的一致性,并且能避免样式表里的一些错误。

Stylelint使用了 PostCSS,所以它能处理任何PostCSS能处理的语法,包括Sass。

PostCSS是一个用JS插件处理样式表的工具。这些插件可以检查你的CSS,支持变量跟mixin,编译未来的CSS语法,以及行内置你的图片等等。

PostCSS的宗旨是只做一件事,并把它做好。所以,它完全是基于插件的。目前,PostCSS有超过200个插件,而且因为它们都是由JavaScirpt实现的,所以速度很快!

在下面的章节里,我们会用PostCSS和Stylelint来检查我们的样式表。

配置

Stylelint配置文件

Sytlelint的魅力之处在于它的灵活。你可以从头开始构建自己的编译规则,所以它灵活不灵活都是你决定的。你完全不需要在开始之前,花很多时间来删除你不需要的规则。

你最好在开始前读一读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文件可以把它们连在一起:

/**
 * 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));
});

怎么样,简单吧?把所有的检查规则跟import加起来,我就写了50多行。你要做的就是把里面源文件的位置改成你自己的项目!

更棒的是,只要改一行代码,就可以支持Sass!让我们来看看怎么做:

怎么检查Sass

用PostCSS检查Sass文件超级简单。检查CSS跟Sass的唯一不同,就是你要让PostCSS能理解.Scss语法。而你要做的,就只是安装postcss-scss并修改一行上面的代码

`npm install postcss-scss --save-dev` 
 //[...]

  return gulp.src(
      ['app/assets/css/**/*.scss',
      '!app/assets/css/vendor/**/*.scss']
    )
    .pipe(postcss(processors, {syntax: syntax_scss})); ⬅
}); 
`npm install gulp-postcss postcss-reporter stylelint postcss-scss --save-dev` 

这里是完整的gulp文件:

/**
 * 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/**/*.scss',
      // Ignore linting vendor assets
      // Useful if you have bower components
      '!app/assets/css/vendor/**/*.scss']
    )
    .pipe(postcss(processors, {syntax: syntax_scss}));
});

超级简单是不是!就只要这样,你现在可以同时检查CSS跟Sass了!

如果你想知道怎么用插件扩展Stylelint以及这么做的原因,请继续阅读,我会用一个例子来讲解。

用插件扩展Stylelint

跟PostCSS一样棒,Stylelint可以用插件扩展。

让我们看一个例子,这个例子里,静态检查可以提高代码的可读性,也可以提醒那些偷懒的程序员,如果他们想用奇怪的方法修改代码来达到目的,并把这样的代码放到代码库里。

例子: 实际使用静态检查

####喜欢写代码的PM 这个例子怎么样。一个PM正管理着一个开发中的web应用,为了节省其他程序员的宝贵时间,他决定自己加一个新功能。这个功能是在鼠标悬停于一个组件上时,给它加一个阴影,并给子组件的链接加一个悬停的状态。

最坏的情况是怎么样的呢?

PM: 相信我,我行的。

眨眼卖萌

把这个代码库给毁了。

— I Am Devloper (@iamdevloper) February 8, 2016

这是那个PM加到项目里的代码:

.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里用嵌套的选择器 —用静态检查!

用Sass开发的时候,嵌套的选择器虽然不好,但是不可避免。如果用得好,它非常有用。但是如果乱用,就会导致代码不可复用 。嵌套常常是偷懒导致的,而偷懒会让代码难以阅读,实现糟糕。第一个&:hover{...}可能在父元素定义的10行以下,这导致你很难看出它属于哪个元素。但是,更关键的是,这里的嵌套完全是没必要的。

上面的规则会被编译成:

.component:hover .component_child ul li a:hover {}
/* 握草什么鬼 */ 

如果我的组里有人把这样的代码提交到代码库里,我真的真的会想骂人。

后来想改这段代码的程序员会很头疼。记着,我建议你绝对不要用嵌套的CSS,除非你知道你自己在干嘛。

好在,有个插件可以解决这个问题!我们可以给Stylelint安装stylelint-statement-max-nesting-depth并设置一个最大嵌套值来避免乱用嵌套。

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

简单地把下面的加入我们的Sass检查任务里,它们就都连起来了:

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

  //[..]
}); 

对于有经验的队伍,我会把最大嵌套设为3。(对于新手,把这个值调小)。

当最大嵌套设置为3之后,Stylelint会提醒PM重写他的代码。PM回去想了想,又写了这样的代码:

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

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

这个重写的版本比原来的好看一点,但是还是不能接受。这里嵌套的选择根本就没必要!静态检查知道这一点,并会让PM重新想直到他能通过检查。

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

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

现在,有点像样了。静态检查会接受这些代码。上面的代码不差,但是还能更好。如果你想特别严格,你可以把所有嵌套全关了,除了@规则。这会逼得所有人,包括PM,在他们写代码的时候,仔细想想他们在做什么。

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

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

不错! 如果没有静态检查样式表,提醒你重构代码,这样的偷懒永远不会被发现,然后代码库的质量就会不断下降。

希望到此为止,我已经让你相信对样式表静态检查是值得的投入。静态检查是你的朋友。你只要投入一点,就可以保证你的队伍不受糟糕代码的影响。

赶紧去用吧,我的程序员以及设计师朋友们!

从此以后,都用静态检查!

后记

一键安装Sass的gulp构建流

你觉得,如果只要安装一个很小的npm模块,就能够免费得到一个完整的构建Sass以及静态检查的工作流怎么样?

我觉得不错。为此,我开源了一个模块,叫Slushie,你可以看看这篇博文去了解它。