让CSS更完美:PostCSS-modules

译者注(GeoffZhu): 这篇适合一些使用过预处理CSS的开发者,好比less,sass或stylus,若是你都没用过,那你必定不是个好司机。在PostCSS中早就可使用CSS Modules了,该篇做者贡献了一个新工具,可让更多开发者方便的使用最新的CSS Modules。css

咱们和全局做用域的css斗争了多年,如今终因而时候结束它了。无论你用的是什么语言仍是框架,CSS命名冲突将再也不是个问题。我将给你展现一下PostCSSPostCSS-modules如何使用,而且能够在服务端使用它们。 CSS起初只是一个美化文档的工具,可是事情到1996年发生了变化。浏览器中再也不单单只有文档了,即时通信,各类软件,游戏,没什么是浏览器不能承载的。html

当今,咱们在HTML和CSS方面已经走了很远很远,开发者们激发出了CSS全部的潜力,甚至创造出了一些CSS自己都快驾驭不了的东西。react

每个有经验的开发者都知道 — 每次使用全局命名空间都是留下了一个产生bug的隐患,由于很快就可能出现相似命名冲突之类的问题,再加上其余方面(项目愈来愈大等)的影响,代码愈来愈不易维护。git

对于CSS来讲,这意味着有问题的布局。CSS特异性和CSS宽泛性之间,一直存在着如史诗般的对决。仅仅是由于每一个选择器均可能会影响到那些不想被影响的元素,使之产生了冲突。github

基本全部编程语言都支持局部做用域。和CSS朝夕相伴的JavaScript有AMD, CommonJS和最终肯定的ES6 modules。可是咱们并无一个能够模块化CSS的方法。编程

对于一个高质量项目来讲,独立的UI组件(也就是组件化)很是重要的 — 每一个组件小巧独立,能够拼合成复杂的页面,这让咱们节省了不少的工做。可是咱们始终有一个疑问,如何防止全局命名冲突那?json

解决方法

由于有前人的探寻,如今咱们有Object-Oriented CSSBEMSMACSS等等,这些都是很是棒而且很是有用的方法。他们经过增长前缀的办法,解决了命名冲突的问题。gulp

经过增长前缀的办法解决命名冲突是个体力活(manual mangling)。咱们手动的去编写长长的选择器。你也可使用预编译的css语言,可是它们并无从根本上解决问题(仍是体力活)。下面是咱们用BEM规范书写的一个独立组件(对于现有的除BEM以外的方法,思想上基本也是这样):浏览器

/* 普通 CSS */
.article {
  font-size: 16px;
}

.article__title {
  font-size: 24px;
}

/* 使用css预处理语言 */
.article {
  font-size: 16px;

  &__title {
    font-size: 24px;
  }
}

CSS模块(CSS Modules)

2015年出现了另外两种方法的实现。分别是CSS-in-JS 和 CSS Modules。咱们将主要谈论后者。sass

CSS模块容许你将全部css class自动打碎,这是CSS模块(CSS Modules)的默认设置。而后生成一个JSON文件(sources map)和本来的class关联:

/* post.css */
.article {
  font-size: 16px;
}

.title {
  font-weight: 24px;
}

上面的post.css将会被转换成相似下面这样:

.xkpka {
  font-size: 16px;
}

.xkpkb {
  font-size: 24px;
}

被打碎替换的classes将被保存在一个JSON对象中:

`{  "article":  "xkpka",  "title":  "xkpkb"  }  `

在转换完成后,你能够直接引用这个JSON对象到项目中,这样就能够用以前写过的class名来直接使用它了。

import styles from './post.json';

class Post extends React.Component {
  render() {
    return (
      <div className={ styles.article }>
        <div className={ styles.title }>…</div>
        …
      </div>
    );
  }
}

更多给力的功能, 能够看看 这篇很是好的文章.

不光是保留了以前提到的几种方法的优势,还自动解决了组件css分离的问题。这就是CSS模块(CSS Modules),听起来很是不错吧!

到这里,咱们有遇到了另外一个问题: 咱们如今的CSS Modules相关工具,只能在客户端(浏览器)使用,把它放到一个非Node.js的服务端环境中是十分十分困难的。

PostCSS-modules

为了在服务端和客户端都能使用CSS Modules,我写了个PostCSS-modules,它是一个PostCSS插件,让你能够在服务端使用模块化的CSS,而且服务端语言能够是Ruby, PHP, Python 或者其余语言。

PostCSS是一个CSS预处理器,它是用JS实现的。它支持静态检查CSS,支持变量和混入(mixins),能让你使用如今还未被浏览器支持的将来CSS语法,内联图像等等。例如使用最为普遍的Autoprefixer,它只是PostCSS的一个插件。

若是你使用Autoprefixer, 其实你早就在用PostCSS了。因此,添加PostCSS-modules到你的项目依赖列表,并非一件难事。我先给你打个样(实例),用Gulp and EJS,其实你能够用任何语言作相似的事情。

// Gulpfile.js
var gulp         = require('gulp');
var postcss      = require('gulp-postcss');
var cssModules   = require('postcss-modules');
var ejs          = require('gulp-ejs');
var path         = require('path');
var fs           = require('fs');

function getJSONFromCssModules(cssFileName, json) {
  var cssName       = path.basename(cssFileName, '.css');
  var jsonFileName  = path.resolve('./build', cssName + '.json');
  fs.writeFileSync(jsonFileName, JSON.stringify(json));
}

function getClass(module, className) {
  var moduleFileName  = path.resolve('./build', module + '.json');
  var classNames      = fs.readFileSync(moduleFileName).toString();
  return JSON.parse(classNames)[className];
}

gulp.task('css', function() {
  return gulp.src('./css/post.css')
    .pipe(postcss([
      cssModules({ getJSON: getJSONFromCssModules }),
    ]))
    .pipe(gulp.dest('./build'));
});

gulp.task('html', ['css'], function() {
  return gulp.src('./html/index.ejs')
    .pipe(ejs({ className: getClass }, { ext: '.html' }))
    .pipe(gulp.dest('./build'));
});

gulp.task('default', ['html']);

咱们只须要执行gulp任务,就能获得转换后的CSS文件和JSON文件,而后就能够在EJS模版里面用了:

<article class="<%= className('post', 'article') %>">
  <h1 class="<%= className('post', 'title') %>">Title</h1>
  ...
</article>

若是你想看看实际的代码,我在GitHub给你准备了个example。更多的例子能够看PostCSS-modulesCSS Modules


轻松编写可维护的CSS,没有臃肿的mixins。长长的前缀将成为历史,欢迎来到将来的CSS世界。

相关文章
相关标签/搜索