從sass到PostCSS

原文鏈接:From Sass to PostCSS

譯者:Icarus

郵箱:xdlrt0111@163.com

多年來我一直使用Sass.但是最近我想要使用PostCSS和它的cssnext插件來嘗試處理樣式.我愛死了現在就可以使用將來的CSS特性,相對於之前我用的工具,它們更順手一些.我的個人站點就是嘗試新特性的最好的測試地.

第一步是列出我Sass用法的清單.我需要知道我使用了哪些特性,並且確信新特性在postCSS中有替代品.以下是我正在這個項目中使用的特性:

  • 部分引用(partial import)

  • 變數(variables)

  • 嵌套(nesting)

  • 混合宏(mixins)

  • 拓展(extend)

  • 佔位類(placeholder classes)

  • 顏色函數(darken and rgba color functions)

  • 壓縮(compression)

準備工作

在切換到新語法之後我需要做一些準備.現在項目的目錄結構是Sass的典型用法.我用下劃線(_)來命名文件,文件的拓展名為scss.我使用兩個文件夾來組織Sass文件.moudules文件夾保存不直接產生CSS的Sass文件,像是變數、佔位類和混合宏.partials保存編譯出CSS的Sass文件.

這是最初的文件結構:

css/n scss/n modules/n _module.scssn ...n partials/n _partial.scssn ...n tylergaw.scssn

每個Sass組件會在tylergaw.scss中引入.

@import "modules/setup";n@import "modules/reset";n@import "modules/fonts";n

我重新組織並且重命名了文件.我先把所有文件的後綴名從scss改為css.我使用了一個Bash腳本來完成這項工作,而不是一個一個修改.

`for f in *.scss; do git mv -- "$f" "${f%.scss}.css"; done;`n

前面的下劃線是編寫Sass的習慣所以我也去掉了它.我沒辦法使用Bash命令一次性完成,所以只能手動每個去修改.

最後一步就是將所有的CSS文件都移動至modules文件夾並且刪除partials文件夾.我認為將所有CSS都當成modules來管理要比將他們按照moudules/partials拆分更容易理解.

環境搭建

我以PostCSS CLI為起始,在package.json里添加了一個臨時的構建腳本命令:

"scripts": {n "postcss": "postcss -o public/css/tylergaw.css src/css/tylergaw.css"n}n

在沒有更改任何樣式的情況下我編譯了CSS:

`npm run postcss`n

正常工作.控制台沒有報錯,但是頁面上沒有任何CSS樣式.

構建過程是可用的,現在的任務是把樣式找回來.

在Chrome的控制台里我看到了很多404信息.這表示我們第一個丟失的特性就是內聯@import.tylergaw.css通過@import來引入CSS模塊.瀏覽器看到這些,知道它要做什麼.瀏覽器會通過HTTP請求來載入每個模塊.我的構建過程只複製了一個獨立的CSS文件,而不是每個模塊.正因如此,瀏覽器找不到它們.

我可以改變構建過程來讓默認的@import工作,但那樣效率很低.我需要一個Sass樣式內聯@import的替代品.

第一個插件

postcss-import插件可以代替Sass中的@import,在通過npm安裝之後,我更新了構建腳本代碼:

"scripts": {n "postcss": "postcss -u postcss-import -o public/css/tylergaw.css src/css/tylergaw.css"n}n

再次運行npm run postcss,單個的CSS文件就包含了所有模塊.現在的頁面就展示出了部分樣式.

這會是CSS的未來嗎?

在Sass中展現出內聯方式的@import功能是非常強大的.它讓我們能更好的組織樣式.我不確定將來這個功能會不會原生支持.我們使用這種功能時總是需要一步編譯,看起來也不壞.

我想postcss-import插件會成為我PostCSS的一個主要配置,對其他人來說應該也一樣.下面引用了插件作者的看法:

This plugin should probably be used as the first plugin of your list. This way, other plugins will work on the AST as if there were only a single file to process, and will probably work as you can expect.

[postcss-import](postcss/postcss-import)n

cssnext

cssnext是PostCSS中一個插件,用於將未來CSS特性編譯為現今支持的特性.特別需要指出,它和Sass或Less並非不同的語言.它提供正在進行中的CSS規範的特性.一些特性已經得到瀏覽器支持.另外一些還處於規範的初始階段.

我使用cssnext來填補失去的Sass特性留下的鴻溝.

瀏覽器私有前綴

在構建這個網站之前我了解過Autoprefixer.我用自定義Sass混合宏來解決添加所需要的前綴的問題.cssnext包含了Autoprefixer,所以我可以將這整個混合宏模塊移除.

變數

下一步我將Sass變數改為CSS自定義屬性.比如在_setup.scss中,我這樣寫:

$grey: #1e1e1d;n$yellow: #ffad15;n$offwhite: #f8f8f8;n$darkerwhite: darken($offwhite, 15);n

這不是所有我使用的Sass變數,但是主要就這些.剩下都在獨立的模塊中.

注意: 自定義屬性和變數的區別.CSS自定義屬性只在屬性值有效,不能用於選擇器,屬性名或媒體查詢.

新的setup.css:

:root {n --white: #fff;n --grey: #1e1e1d;n --yellow: #ffad15;n --offwhite: #f8f8f8;n ...n}n

以下為使用示例:

a {n color: var(--yellow);n}n

除了語法,CSS自定義屬性和Sass變數工作方式是相同的.由於瀏覽器支持的限制,自定義屬性值仍然需要編譯.在上面的示例中,編譯後的值為color: #ffad15.

顏色函數

在之前的例子中,我遺漏了一個變數:$darkerwhite: darken($offwhite, 15);.這是另一個我需要尋找替代的Sass特性.這裡有一個規範草案提供CSS顏色函數.cssnex現在包含這些函數,這非常酷.下面是setup.css,其中darkerwhite自定義屬性是通過顏色函數和陰影調節器來實現的.

:root {n ...n --offwhite: #f8f8f8;n --darkerwhite: color(var(--offwhite) shade(20%));n ...n}n

顏色函數提供了許多調節器.你可以在一個函數中使用多個調節器:

`background-color: color(#d32c3f shade(40%) alpha(40%));`n

編譯結果為:

`background-color: rgba(127, 26, 38, 0.4);`n

再次重申,現在cssnext會將color()編譯為16進位或rgba的色值.當顏色函數得到瀏覽器支持後,編譯過程就沒有必要了.顏色操作在運行時就可以發生.

嵌套

嵌套是CSS預處理器不可或缺的特性.任何讓人舒服的樣式工具的必需品.Tab Atkins對CSS嵌套有一個正在進行中的規範,並且cssnext讓它成為現實.

CSS的嵌套語法包含一個前置於內層的&,以下為sass片段:

.projects-list {n ...nn li {n & > div {...}n }nn a {n ...nn &:hover,n &:focus {...}nn &::after {...}n }nn @media (min-width: 640px) {...}n}n

對於CSS嵌套,我將它修改為以下形式:

.projects-list {n ...nn & li {n & > div {...}n }nn & a {n ...nn &:hover,n &:focus {...}nn &::after {...}n }nn @media (min-width: 640px) {...}n}n

基本的嵌套需要前置的&.偽類和選擇器在Sass和CSS中是相同的.媒體查詢不需要前置&.

另外值得注意的是@nest.正如文檔中提到的,複雜的嵌套可能需要引入@nest來代替&.這個項目我還沒有用到,或許將來用得到.

拓展和佔位類

Sass中的@extend和佔位類是我經常使用的兩個特性。下面是Futura頭部的樣式示例:

%futura {n font-family: futura-pt, helvetica, sans-serif;n}nn%futura-heading {n @extend %futura;n font-weight: 700;n line-height: 1.1;n text-transform: uppercase;n}n

這是一個用例:

.my-heading {n @extend %futura-heading;n}n

我在之前了解過CSS自定義屬性的用法。這裡有一個正在進行中的@apply規則的規範與之相關。@apply允許儲存一系列的屬性並且在選擇器引用。我用@apply來代替Sass的extend.

回到setup.css來,我更新了Futura頭部的屬性:

:root {n ...nn --franklin: {n font-family: futura-pt, helvetica, sans-serif;n };nn --franklin-heading: {n @apply --franklin;n font-weight: 700;n line-height: 1.1;n text-transform: uppercase;n };n}n

這裡是一個示例:

.my-heading {n @apply --franklin-heading;n}n

@apply不是繼承.在目前的cssnext中,@apply將屬性和值直接複製到每條規則中.這是個小項目所以沒問題.但是在大型的項目中,可能會導致樣式冗餘,項目非常臃腫.這種情況下最好還是使用通用類名來適用相似情況.

現在我的網站看起來和之前一樣了.項目頁是個例外.它的每個磁貼區域都有不同顏色.接下來我會解釋怎麼在沒有Sass的情況下正確且高效的編寫樣式.

帶參數的混合宏

我用Sass的混合宏來讓項目編寫樣式更簡便.這個混合宏有一個磁貼顏色的參數.以下是這個project-block的混合宏.

@mixin project-block ($c) {n background-color: $c;nn a {n color: $c;nn &:hover {n background-color: $c;n color: $offwhite);n }n }n}n

下面是一個示例:

.p-jribbble {n @include project-block(#ff0066);n}n

在寫這篇文章的時候,我還沒有在CSS找到能模擬這個功能的特性.自定義屬性配合@apply不是函數,所以我們不能為它傳遞參數.在將來,自定義選擇器可能會允許使用參數.在草案規範中有一個看起來很有前途的複雜示例.但我承認現在我還沒完全明白它是怎麼工作的.

這不意味著我運氣不好.我寫CSS的時間要長於Sass,但也沒多久.我還用了另一個正進行中的規範特性,matches選擇器.

下面是一個代替project-block混合宏的CSS示例:

.p-jribbble,n.p-jribbble a:matches(:hover, :focus) {n background-color: var(--color-jrb);nn & a {n color: var(--color-jrb);n }n}n

顏色變數是早些在文件中:root作用域定義的.cssnext將以上CSS編譯為:

.p-jribbble,n.p-jribbble a:hover,n.p-jribbble a:focus {n background-color: #ff0066n}nn.p-jribbble a,n.p-jribbble a:hover a,n.p-jribbble a:focus a {n color: #ff0066;n}n

最後兩個選擇器...a a:hover和...a a:focus匹配不到任何元素.他們是不必要的.但是除了佔用幾比特的空間他們也沒有任何影響.為了代碼的可讀性,我更傾向於a選擇器的嵌套.

更多PostCSS特性

為了樣式按順序回歸,我決定利用更多的PostCSS插件.我用css mqpacker來合併使用相同查詢條件的媒體查詢.我也用cssnano來優化代碼.

這也是為什麼我期待去使用PostCSS.使用Sass的時候我感覺困在當前的特性中.但因為PostCSS本質是一個插件集合在工作,更具拓展性.如果我有特殊需要,我可以自己來寫一個插件.它的潛力令人興奮.

我妥協了

在使用這個新工具工作了幾天後,我完全投入進去了.從Sass轉向新的CSS語法非常簡單,並且是在五六年間我每個項目都用Sass編寫的情況下.

我喜歡這個思想轉變.cssnext對CSS的處理很像Babel對Javascript.它們都允許你去使用未來的特性來編寫代碼.

推薦閱讀:

Isomorphic 構建基礎
StackBlitz - 針對 Web 開發者的在線 IDE
前端leader們如何安排面試?
精讀《30行js代碼創建神經網路》
PHP 是做前台還是後台?前端和前台的區別?

TAG:PostCSS | CSS | 前端开发 |