讓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世界。

相關文章
相關標籤/搜索