提到css預編譯器(css preprocessor),你可能想到Sass、Less以及Stylus。而本文要介紹的PostCSS,正是一個這樣的工具:css預編譯器可以做到的事,它同樣可以做到。
“你說的我都懂,那為什么要用它?”
套裝與單件
如果Sass等預編譯器是新定義了一種模板語言,然后將其轉化為css的話,PostCSS則是更純粹地對css本身做轉換。
回想一下你是如何學習使用css預編譯器的:了解到有這樣一種可以轉化為css的語言,它有很多特性,變量、嵌套、繼承等等,每一種特性都通過一定語法實現。大概就像是遞給你一個已經封裝好的工具箱(量產型?),你可以在里面找有用的東西。
那PostCSS是怎樣呢?PostCSS就像只遞給你一個盒子,但告訴你你可以從旁邊的陳列櫃取走自己想要的工具放進盒子打包帶走。如果你覺得陳列櫃里的不夠好,PostCSS還可以幫你打造你自己的工具。所以,使用PostCSS,你可以僅取所需。
這就是PostCSS的模塊化(modular)風格。它作為一個css轉換工具,自身很小,其所有的轉換功能都是插件,因此可以個性化配置。
PostCSS的簡要原理
PostCSS自身只包括css分析器,css節點樹API,source map生成器以及css節點樹拼接器。
css的組成單元是一條一條的樣式規則(rule),每一條樣式規則又包含一個或多個屬性&值的定義。所以,PostCSS的執行過程是,先css分析器讀取css字符內容,得到一個完整的節點樹,接下來,對該節點樹進行一系列轉換操作(基於節點樹API的插件),最后,由css節點樹拼接器將轉換后的節點樹重新組成css字符。期間可生成source map表明轉換前后的字符對應關系:
比較有意思的是,PostCSS的插件其實都是JavaScript函數,它們使用PostCSS的節點樹API,對css節點樹進行不同的轉換。
插件預覽
所有插件都可以在PostCSS的主頁中查詢到,這里只選取一小部分示意一下。
Autoprefixer
PostCSS最有名的插件是Autoprefixer。如名所示,可以自動為你添加瀏覽器私有前綴。它的添加值會參考Can I Use及你設定的瀏覽器支持范圍,因此相當可靠。下面是一個示例(以我設定的瀏覽器支持范圍):
.container{ display: flex; }
編譯后:
.container{ display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; }
postcss-nested&postcss-mixins
在剛開始使用PostCSS時,我就想到要用PostCSS實現我在Sass中最常用的特性。所以,我找到了postcss-nested
和postcss-mixins
。將它們結合起來后,就可以做到這樣:
@define-mixin clearfix{ &:after{ display: table; clear: both; content: " "; } } .column-container{ color: #333; @mixin clearfix; }
編譯后:
.column-container{ color: #333; } .column-container:after{ display: table; clear: both; content: " "; }
到這里,你是否已經有了“預編譯器可以做到的它也可以做到”的感覺呢?
如何使用PostCSS
我個人推薦結合Gulp使用,本文在此只介紹gulp-postcss的用法。
gulp-postcss
及插件都是npm,首先,使用npm install
將它們分別安裝到項目目錄中(會位於node_modules
)。然后,編輯glupfile.js
,將PostCSS注冊為Gulp的一個任務。以下是一個結合使用了Autoprefixer
、postcss-simple-vars
、postcss-mixins
、postcss-nested
4個插件,且生成source map文件的例子:
var gulp = require("gulp"); var postcss = require("gulp-postcss"); var autoprefixer = require('autoprefixer-core'); var postcssSimpleVars = require("postcss-simple-vars"); var postcssMixins = require("postcss-mixins"); var postcssNested = require("postcss-nested"); var sourcemaps = require("gulp-sourcemaps"); // Css process. gulp.task("postcss", function(){ var processors = [ postcssMixins, postcssSimpleVars, postcssNested, autoprefixer({ browsers: ["Android 4.1", "iOS 7.1", "Chrome > 31", "ff > 31", "ie >= 10"] })]; return gulp.src(["./stylesheets/src/*.css"]) .pipe(sourcemaps.init()) .pipe(postcss(processors)) .pipe(sourcemaps.write(".")) .pipe(gulp.dest("./stylesheets/dest")); });
在上面這段代碼中,processors
是一個數組,定義了用到的PostCSS插件。PostCSS會按照定義順序依次執行插件,因此,在結合多個插件使用時,請注意它們的位置。
自定義轉換
此外,你可以很容易地創建你自己的轉換(還記得前面說過PostCSS的插件都是JavaScript函數吧?)。例如,下面是一個自定義的轉換方法,它將css代碼中的帶有rem
單位的值,更改為px
的值。
var custom = function(css, opts){ css.eachDecl(function(decl){ decl.value = decl.value.replace(/\d+rem/, function(str){ return 16 * parseFloat(str) + "px"; }); }); };
然后,你將這個方法直接添加到processors
中(就像postcssMixins
那些那樣)就可以使用。如果原來有值是3rem
,將變成48px
。
以上只是一個簡單的轉換,如果要正式做一個插件,請參考PostCSS插件指南。
性能
PostCSS宣稱,由JavaScript編寫的PostCSS比C++編寫的libsass(Sass原本是Ruby編寫的,但后來出了C++的引擎,也就是libsass,它更快)還要快3倍。這里的具體數字我覺得不用多關心,可以感受到“PostCSS的運行速度很快”就足夠了。
實際運行起來大概這樣:
做到更多
基於PostCSS,可以做到許多現有的css預編譯器做不到的事。例如,插件系列cssnext可以讓你使用CSS4+的語法(增加了變量等許多特性),它會幫你轉化為目前可用的CSS3。
一點問題
PostCSS有一個問題,那就是它是零散的,所以我無法找到一個編輯器能正確地解析並高亮准備使用PostCSS的css代碼。例如在WebStorm中我把它當做普通的css文件,結果就會收到很多紅色的錯誤提示。
所以,css預編譯器過時了嗎?
當然不會。就像其他流行的框架和工具那樣,css預編譯器是已經驗證過的可用工具,我們完全可以根據需要選用。
Sass等css預編譯器的特點是成熟可靠。一方面,它們已經是流行的模板語言,有完善的文檔和周邊支持,相對穩定,新加入團隊的人也能比較容易地理解。另一方面,集成的風格有它的方便之處,就像你可能會懶得去組裝一個模型,但能找到專業的人替你完成。
PostCSS的特點則是模塊化。從長遠來看,PostCSS可以做到更多類型的css轉換。而可定制的風格非常適合追求個性的人(更快捷,而且可以自己做出很有趣的插件)。
此外,css預編譯器和PostCSS可以協同使用。有一個流行的用法就是Sass編譯后再接PostCSS的Autoprefixer
(畢竟這是PostCSS的招牌插件)。
結語
PostCSS的風格可以說是在打造一個改變css開發方式的生態系統。所以如果說到未來,還是挺期待的。
(重新編輯自我的博客,原文地址:http://acgtofe.com/posts/2015/05/modular-transforming-with-postcss/)