[轉]CSS 模塊

CSS 模塊 javascript

若是你想知道 CSS 最近發展的轉折點,你應該選擇去觀看 Christopher Chedeau 在2014年11月的 NationJS 大會上作的名稱爲 CSS in JS 的分享。不得不說這是一個技術分水嶺的時刻,一羣不一樣的思惟在本身的方向上就像粒子進入了高能漩渦中同樣飛速發展。其中,在 React 或者是 React 相關的項目中編寫 CSS 樣式,React Style, jxstyleRadium 這三個算是最新的,最好的以及最具備可行性的方法。若是說「發明」是探索 最接近的可能 的一個實例(譯者注:最接近的多是 Steven Johnson 於 2010 年提出來的一個概念),那麼 Christopher 則是讓許許多多的可能變得更加接近(譯者注:上面三個工具中的兩個靈感都是來自他的分享)。 css

This slide really hit home for a lot of folks

上圖列出的這些都是在許多大型 CSS 代碼庫中存在的問題。Christopher 指出,只要將你的樣式經過用 JS 去管理,這些問題都能很好的解決。不得不說這的確是有道理的,可是這種方法有它的複雜性並會帶來其餘的相關問題。其實只要看看瀏覽器是如何處理 :hover 僞類狀態的,咱們就會發現有些東西在 CSS 中其實很早就解決了。 CSS 模塊小組 以爲咱們能夠更加合理的解決問題:咱們能夠繼續保持 CSS 如今的樣子,並在 styles-in-JS 社區的基礎上創建更合理的改進。雖然咱們已經找到了解決辦法同時又捍衛了 CSS 原始的美,但咱們仍然欠那些把咱們推向這個結果的那些人一聲感謝。謝謝大家,朋友們! 下面讓我來向你們解說一下什麼是 CSS 模塊而且爲何它纔是將來吧。 html

咱們是如此的強烈的思考着 CSS 的將來

第一步:像局部同樣無需考慮全局衝突

在 CSS 模塊中,每個文件被編譯成獨立的文件。這樣咱們就只須要使用通用簡單的類選擇器名字就行了。咱們不須要擔憂它會污染全局的變量。下面我就咱們編寫一個擁有四個狀態的按鈕來講明這個功能。 java

https://jsfiddle.net/pqnot81c/embedded/result/ react

CSS 模塊以前咱們是怎麼作的

咱們也許會使用 Suit/BEM 命名規範去進行樣式命名,這樣咱們的 HTML 和 CSS 代碼看起來就像以下所示:webpack

/* components/submit-button.css */
.Button { /* all styles for Normal */ }
.Button--disabled { /* overrides for Disabled */ }
.Button--error { /* overrides for Error */ }
.Button--in-progress { /* overrides for In Progress */

這樣寫看起來還挺棒的。使用 BEM 命令方式使咱們有了 4 個樣式變量這樣咱們沒必要使用嵌套選擇器。使用Button這種首字母大寫的方法能夠很好的避免與以前的代碼或者是其餘的依賴代碼進行衝突。另外咱們使用了--語法這樣能很清楚的顯示出咱們的依賴 Class。 總的來講,這樣作可讓咱們的代碼更易於維護,可是它須要咱們在命名規範的學習上付出不少努力。不過這已是目前 CSS 能給出的最好的解決辦法了。 git

CSS 模塊出來以後咱們是怎麼作的

CSS 模塊意味着你今後不再必爲你的名字太大衆而擔憂,只要使用你以爲有最有意義的名字就行了:github

/* components/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */

請注意咱們這裏並無在任何地方使用 button 這個詞。不過反過來想,爲何咱們必定要使用它呢?這個文件已經被命名成了 submit-button.css 了呀!既然在其它的語言中你不須要爲你的局部變量增長前綴,沒道理 CSS 須要加上這個蹩腳的功能。 經過使用 require 或者 import 從 JS 中導入文件使得 CSS 模塊被編譯成爲可能。web

/* components/submit-button.js */
import styles from "./submit-button.css";
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>

你沒必要擔憂大衆名字會致使命名衝突,編譯後實際上類名是會自動生成並保證是惟一的。CSS 模塊爲你作好一切,最終編譯成一個 CSS 與 JS 交互的ICSS 後綴文件(閱讀這裏瞭解更多)。所以,你的程序最終看起來可能會是這個樣子的:bootstrap

<button class="components_submit_button__normal__abc5436">
  Processing...
</button>

若是你的類名變的和上面的例子差很少的話,那麼恭喜你你成功了!

Success

命名約定

如今回過頭來仔細看看咱們的示例代碼:

/* components/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */

請注意全部的類都是相互獨立的,這裏並不存在一個「 基類 」而後其它的類集成並「 覆蓋 」它的屬性這種狀況。在 CSS 模塊中 每個類都必須包含這個元素須要的全部樣式 (稍後會有詳細說明)。這使得你在 JS 中使用樣式的時候有很大的不一樣:

/* Don"t do this */
`class=${[styles.normal, styles["in-progress"]].join(" ")}`

/* Using a single name makes a big difference */
`class=${styles["in-progress"]}`

/* camelCase makes it even better */
`class=${styles.inProgress}`

固然,若是你的工資是按照字符串長度來計算的話,你愛怎麼作就怎麼作吧!

React 示例

CSS 模塊並非 React 特有的功能,可是不得不說在 React 中使用 CSS 模塊會更爽。基於這個理由,我以爲我有必要展現下面這個如飄柔般絲滑的示例:

/* components/submit-button.jsx */
import { Component } from "react";
import styles from "./submit-button.css";

export default class SubmitButton extends Component {
  render() {
    let className, text = "Submit"
    if (this.props.store.submissionInProgress) {
      className = styles.inProgress
      text = "Processing..."
    } else if (this.props.store.errorOccurred) {
      className = styles.error
    } else if (!this.props.form.valid) {
      className = styles.disabled
    } else {
      className = styles.normal
    }
    return <button className={className}>{text}</button>
  }
}

你徹底不須要擔憂你的類命名會和全局的樣式表命名衝突,這樣能讓你的注意力更集中在組件上,而不是樣式。我敢保證,使用過一次以後,你會不再想回到原來的模式中去。 然而這僅僅是一切的開始。CSS 模塊化是你的基本,但也是時候來思考一下如何把你的樣式們都集中到一塊了。

第二步:組件就是一切

上文中我提到了每個類必須包含按鈕不一樣狀態下的全部的樣式,與 BEM 命名方式上相比,代碼上可能區別以下:

/* BEM Style */
innerHTML = `<button class="Button Button--in-progress">`

/* CSS Modules */
innerHTML = `<button class="${styles.inProgress}">`

那麼問題來了,你怎麼在全部的狀態樣式中共享你的樣式呢?這個答案就是 CSS 模塊的強力武器 - 組件

.common {
  /* all the common styles you want */
}
.normal {
  composes: common;
  /* anything that only applies to Normal */
}
.disabled {
  composes: common;
  /* anything that only applies to Disabled */
}
.error {
  composes: common;
  /* anything that only applies to Error */
}
.inProgress {
  composes: common;
  /* anything that only applies to In Progress */
}

composes這個關鍵詞將會使.normal類將.common內的全部樣式包含進來,這個有點像 Sass 的 @extends 語法。可是 Sass 依賴重寫你的 CSS 文件達到效果,而 CSS 模塊最後會經過 JS 編譯導出,不須要修改文件(譯者注:下面會有例子詳細說明)。

Sass

按照 BEM 的命名規範,我用 Sass 的 @extends 寫的話可能會像以下的代碼:

.Button--common { /* font-sizes, padding, border-radius */ }
.Button--normal {
  @extends .Button--common;
  /* blue color, light blue background */
}
.Button--error {
  @extends .Button--common;
  /* red color, light red background */
}

編譯後的 CSS 文件以下:

.Button--common, .Button--normal, .Button--error {
  /* font-sizes, padding, border-radius */
}
.Button--normal {
  /* blue color, light blue background */
}
.Button--error {
  /* red color, light red background */
}

你能夠只須要個類來標記你的元素<button class="Button--error">,可是你能獲得包括共有的和特例的全部樣式。這看起來真的很是厲害,可是你須要注意這種語法在一些特別的狀況下存在問題和陷阱。Hugo Giraudel 在他的文章中作了很好的總結,感謝他!

CSS 模塊

composes 語法看起來很像 @extends 可是他們的工做方式是不一樣的。爲了演示一下,讓咱們來看一個例子:

.common { /* font-sizes, padding, border-radius */ }
.normal { composes: common; /* blue color, light blue background */ }
.error { composes: common; /* red color, light red background */ }

編譯後的文件多是像以下同樣:

.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 { /* blue color, light blue background */ }
.components_submit_button__error__1638bcd { /* red color, light red background */ }

JS 代碼中經過 import styles from "./submit-button.css" 最終會返回:

styles: {
  common: "components_submit_button__common__abc5436",
  normal: "components_submit_button__common__abc5436 components_submit_button__normal__def6547",
  error: "components_submit_button__common__abc5436 components_submit_button__error__1638bcd"
}

因此咱們依然可使用 style.normal 或者 style.error 在咱們的代碼中,仍舊會有多個類樣式渲染在咱們的 DOM 上

<button class="components_submit_button__common__abc5436 
               components_submit_button__normal__def6547">
  Submit
</button>

這就是 composes 語法的厲害之處,你能夠在不重寫你的 CSS 的狀況下對你的元素混合使用不一樣類的樣式。

第三步:文件間共享

Sass 或者 LESS 中,你能夠在每一個文件中使用 @import 在全局工做區間內共享樣式。這樣你就能夠在一個文件中定義變量或者函數(mixins)並在你的其它組件文件中共享使用。這樣作是有好處的,可是很快你的變量命名就會與其它的變量名稱相沖突(雖然它在另一個所有空間下),你不可避免的會重構你的 variables.scss 或者 settings.scss,最後你就會發現你已經看不懂到底哪一個組件依賴哪一個變量了。最後的最後你會發現你的配置文件變量名稱冗餘到變得很是不實用。 針對上述問題仍然是有更好的解決辦法的(事實上 Ben Smithett 的文章 Sass 和 Wepack 的混合使用 給了 CSS 模塊話很大的啓發,我推薦你們去讀一讀這篇文章),可是無論怎麼作你仍是侷限在了 Sass 的全局環境下。 CSS 模塊一次只運行一個文件,這樣能夠避免全局上下文的污染。並且像 JS 使用 import 或者 require 來加載依賴同樣,CSS 模塊使用 compose 來從另外一個文件中加載:

/* colors.css */
.primary {
  color: #720;
}
.secondary {
  color: #777;
}
/* other helper classes... */
/* submit-button.css */
.common { /* font-sizes, padding, border-radius */ }
.normal {
  composes: common;
  composes: primary from "../shared/colors.css";
}

使用組件,咱們可以深刻到每個像colors.css同樣的基礎樣式表中,並隨意重命名它。又由於組件只是改變了最後導出的類名稱,而不是 CSS 文件自己,composes 語句在瀏覽器解析以前就會被刪除。

/* colors.css */
.shared_colors__primary__fca929 {
  color: #720;
}
.shared_colors__secondary__acf292 {
  color: #777;
}
/* submit-button.css */
.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 {}
<button class="shared_colors__primary__fca929
               components_submit_button__common__abc5436 
               components_submit_button__normal__def6547">
  Submit
</button>

事實上,當它被瀏覽器解析以後,咱們的局部是不存在一個"normal"樣式的。這是一件好事!這意味着咱們能夠增長一個局部名字有意義的對象(可能就叫"normal")而不用在 CSS 文件中新增代碼。咱們使用的越多,對咱們的網站會形成更少的視覺偏差以及在用戶瀏覽器上更少的不一致。 題外話:空的類樣式可使用 csso 這樣的工具來檢查去除掉。

第四步:功能單一模塊

組件是很是強大的,由於它確實的讓你描述了一個元素是什麼,而不是它由那些樣式組成。這是一種不一樣的方式去描述概念示例(元素)到樣式實體(樣式規則)之間的映射關係。讓咱們看一個簡單的 CSS 例子:

.some_element {
  font-size: 1.5rem;
  color: rgba(0,0,0,0);
  padding: 0.5rem;
  box-shadow: 0 0 4px -2px;
}

這些元素,樣式都是特別簡單的。然而也存在着問題:顏色,字體大小,盒子陰影,內邊距-這裏的一切都是量身定製的,這讓咱們想要在其它地方複用這些樣式的時候變得有些困難。下面讓咱們用 Sass 重構這些這些代碼:

$large-font-size: 1.5rem;
$dark-text: rgba(0,0,0,0);
$padding-normal: 0.5rem;
@mixin subtle-shadow {
  box-shadow: 0 0 4px -2px;
}

.some_element {
  @include subtle-shadow;
  font-size: $large-font-size;
  color: $dark-text;
  padding: $padding-normal;
}

這是一個進化版,可是咱們僅僅只達到了一部分目標。事實上 $large-font-size$padding-normal 只是在名字上表示了它的用途,並不能在任何地方都執行。像 box-shadow 這種定義沒辦法賦值給變量,咱們不得不使用 mixin 或者 @extends 語法來配合。

CSS 模塊

經過使用組件,咱們可使用咱們可複用的部分定義咱們的組件:

.element {
  composes: large from "./typography.css";
  composes: dark-text from "./colors.css";
  composes: padding-all-medium from "./layout.css";
  composes: subtle-shadow from "./effect.css";
}

這種寫法勢必會有不少單一功能文件產生,然而經過使用文件系統來管理不一樣用途的樣式比起用命名空間來講要好的多。若是你想要從一個文件中導入多個類樣式的話,有一種簡單的寫法:

/* this short hand: */
.element {
  composes: padding-large margin-small from "./layout.css";
}

/* is equivalent to: */
.element {
  composes: padding-large from "./layout.css";
  composes: margin-small from "./layout.css";
}

這開闢了一種可能,使用極細粒的類樣式定義一些樣式別名去給每個網站使用:

.article {
  composes: flex vertical centered from "./layout.css";
}

.masthead {
  composes: serif bold 48pt centered from "./typography.css";
  composes: paragraph-margin-below from "./layout.css";
}

.body {
  composes: max720 paragraph-margin-below from "layout.css";
  composes: sans light paragraph-line-height from "./typography.css";
}

我對這種技術很是感興趣,我以爲,它混合了像 Tachyons 相似的 CSS 原子技術,像 Semantic UI 那樣變量分離可讀的好處(譯者注:說人話就是說 CSS 模塊的命名簡單易懂,組件複用方便)。 可是 CSS 模塊化之路僅僅是剛剛開始,將來咱們但願你們能嘗試更多爲它寫出譜寫出新的篇章。

翻滾吧!CSS 模塊!

咱們但願 CSS 模塊化能有助於你和你的團隊在大家現有的 CSS 和產品的基礎上維護代碼,讓它變得更溫馨更高效。咱們已經接近可能的把額外的語法減小到最少,並儘可能確保語法和現有的變化不大。咱們有 WebpackJSPMBrowserify 的示例代碼,若是大家使用它們中的其中之一,大家能夠參考一二。固然咱們也在爲 CSS 模塊話的使用尋找新的環境:服務端 NodeJS 的支持正在進行,Rails 的支持已經提上議程準備進行了。 可是爲了讓它變得更簡單,我在 Plunker 上製做了一個預覽示例,你不用安裝任何東西就能夠運行它:css_modules_plunkr.png這裏是 CSS 模塊的 Github 倉庫地址,若是你有任何問題,請開 issue 提出。CSS 模塊團隊還很小,咱們尚未看到一些有用的例子,歡迎大家給咱們投稿。

最後的最後,祝你們開心寫樣式,幸福每一天!

原文:http://glenmaddern.com/articles/css-modules

本文連接:http://www.75team.com/post/1049.html

-- EOF --

發表於 2015-08-21 20:34:48 ,最後修改於 2016-03-15 16:31:48

« 「文字」聚合、散出動畫用Node.js開發微信牆簡明教程 »

Comments

© 2016 -  奇舞團博客  - www.75team.com
Powered by ThinkJS & FireKylin

相關文章
相關標籤/搜索