原文地址:react-css-modules
閱讀本文前建議瞭解 CSS Modules 的知識。牆裂推薦閱讀 Cam 的文章 CSS Modules詳解及React中實踐javascript
React CSS Modules 實現了自動化映射 CSS modules。每一個 CSS 類都被賦予了一個帶有全局惟一名字的本地標識符。 CSS Modules 實現了模塊化和複用性。(https://github.com/minooo/React-Study/tree/master/step-02)css
CSS Mosules 碉堡了。若是你對 CSS Modules 還不夠熟悉,那麼不要緊,它只是一個使用 webpack 之類的模塊打包機加載 CSS 做用於特定文檔的概念。CSS module loader 將爲每個 CSS 類在加載 CSS 文檔(確切的說,這個文檔就是Interoperable CSS)的時候生成一個惟一的名字。你能夠來看下這個 CSS Modules 實踐例子——webpack-demo。html
在React語法環境中,CSS Modules 看起來是這樣子的:java
import React from 'react'; import styles from './table.css'; export default class Table extends React.Component { render () { return <div className={styles.table}> <div className={styles.row}> <div className={styles.cell}>A0</div> <div className={styles.cell}>B0</div> </div> </div>;
組件渲染出來後會生成相似於這樣的一個標記:react
<div class="table__table___32osj"> <div class="table__row___2w27N"> <div class="table__cell___2w27N">A0</div> <div class="table__cell___1oVw5">B0</div> </div> </div>
同時也會生成對應的匹配那些CSS類的CSS文件,是否是碉堡了?!webpack
css-loader
CSS Modules 是一個能夠被多種方法實現的規範。react-css-modules
利用 webpack css-loader 所提供的功能啓用了現有的 CSS Modules 的集成。git
webpack 的 css-loader 自己有幾處劣勢:github
你必須使用駝峯式類名。web
不管什麼時候構建一個 className
你都必須使用 style
對象。segmentfault
混合類模塊以及全局 CSS 類不夠靈活。
引用一個未定義的 CSS 模塊時解析結果爲 undefined
,但並沒有相關警告提示。
React CSS Modules 組件自動加載應用了 styleName
特性的 CSS Modules ,例如:
import React from 'react'; import CSSModules from 'react-css-modules'; import styles from './table.css'; class Table extends React.Component { render () { return <div styleName='table'> <div styleName='row'> <div styleName='cell'>A0</div> <div styleName='cell'>B0</div> </div> </div>; } } export default CSSModules(Table, styles);
使用 react-css-modules
好處多多:
你不用再被強制使用駝峯式命名規則
你沒必要每次使用一個 CSS 模塊時還要引用 styles
對象。
有一個明顯的區別在全局 CSS 和 CSS Modules 之間,示例以下:
<div className='global-css' styleName='local-module'></div>
當 styleName
引用一個爲定義的 CSS Module 時,你會獲得一個警告信息。(errorWhenNotFound
選項)
你能夠爲每個 ReactElement
只使用單獨的 CSS Module。(allowMultiple
選項)。
react-css-modules
擴展了目標組件的 render
方法。它將根據 styleName
的值在關聯的 style
對象中查找對應的 CSS Modules,併爲 ReactElement className
屬性值添加相匹配的獨一無二的 CSS 類名。
碉堡了!
你能夠參照下這個例子進一步加深印象,react-css-modules-examples。
設置包括:
設置一個 module bundler
加載 Interoperable CSS。
使用 react-css-modules
修整你的組件。
module bundler
呢?開發模式
開發環境下,若你想啓用 Sourcemaps,並要使用 webpack 的 Hot Module Replacement (HMR,熱替換)。style-loader
已經支持 HMR。所以,Hot Module Replacement 開箱即用。
設置步驟:
安裝 style-loader
。
安裝 css-loader
。
設置 /.css$/
加載器,以下:
{ test: /\.css$/, loaders: [ 'style?sourceMap', 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]' ] }
生產模式
在生產環境中,若是你想把CSS單獨提取出來的話,你須要瞭解這樣作的好處和壞處。
優勢:
更少的樣式標籤
CSS SourceMap
CSS 並行請求
CSS 緩存分離
頁面渲染更快(更少的代碼以及更少的 DOM 操做)
缺點:
額外的 HTTP 請求
較長的編譯時間
較複雜的配置
不支持變動運行環境公共路徑
不支持 Hot Module Replacement
設置步驟:
安裝 style-loader
安裝 css-loader
使用 extract-text-webpack-plugin
提取 CSS 到一個單獨的樣式表。
設置 /.css$/
加載器:
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]') }
設置 extract-text-webpack-plugin
插件:
new ExtractTextPlugin('app.css', { allChunks: true })
完整實例請參照 webpack-demo 或者 react-css-modules-examples。
Browserify(若是你是使用這個構建工具的話)
請參考 css-modulesify。
使用 styles
屬性重寫默認的組件樣式。
示例以下:
import React from 'react'; import CSSModules from 'react-css-modules'; import styles from './table.css'; class Table extends React.Component { render () { return <div styleName='table'> <div styleName='row'> <div styleName='cell'>A0</div> <div styleName='cell'>B0</div> </div> </div>; } } export default CSSModules(Table, styles);
在這個例子中,CSSModules
被用來美化 Table
組件經過引用 ./table.css
CSS 模塊。當 Table
組件渲染完畢,它將使用 styles
對象的屬性構建 className
的值。
使用 styles
屬性你能夠覆蓋組件默認的 styles
對象。例如:
import customStyles from './table-custom-styles.css'; <Table styles={customStyles} />;
Interoperable CSS 能夠擴展其餘 ICSS。利用這個功能能夠擴展默認樣式,例如:
/* table-custom-styles.css */ .table { composes: table from './table.css'; } .row { composes: row from './table.css'; } /* .cell { composes: cell from './table.css'; } */ .table { width: 400px; } .cell { float: left; width: 154px; background: #eee; padding: 10px; margin: 10px 0 10px 10px; }
在這個例子中,table-custom-styles.css
有選擇的擴展了 table.css
(Table
組件的默認樣式)。
這裏是一個更直觀的實踐例子:UsingStylesProperty example`。
style
屬性包裝過的組件繼承了 styles
屬性,該屬性描述了 CSS 模塊和 CSS 類之間的映射關係。
class extends React.Component { render () { <div> <p styleName='foo'></p> <p className={this.props.styles.foo}></p> </div>; } }
在上面示例中,styleName='foo'
和 className={this.props.styles.foo}
是等價的。styles
屬性是爲 Loops and Child Components
實現組件包裝而特地設計的!
styleName
不能去定義一個由其餘組件生成的 React元素的樣式 。例如:
import React from 'react'; import CSSModules from 'react-css-modules'; import List from './List'; import styles from './table.css'; class CustomList extends React.Component { render () { let itemTemplate; itemTemplate = (name) => { return <li styleName='item-template'>{name}</li>; }; return <List itemTemplate={itemTemplate} />; } } export default CSSModules(CustomList, styles);
上面的實例將不會工做。CSSModules 被用來包裝 CustomList
組件。然而,它是呈現 itemTemplage
的列表組件。
爲了解決這個問題,包裝過的組件繼承了樣式屬性,這樣你能夠將它做爲一個常規的 CSS 模塊對象來使用。
所以,前面的例子能夠改寫爲這樣:
import React from 'react'; import CSSModules from 'react-css-modules'; import List from './List'; import styles from './table.css'; class CustomList extends React.Component { render () { let itemTemplate; itemTemplate = (name) => { return <li className={this.props.styles['item-template']}>{name}</li>; }; return <List itemTemplate={itemTemplate} />; } } export default CSSModules(CustomList, styles);
在把子組件傳遞給渲染組件以前,若是你使用了 CSSMmodules
包裝這個子組件,那麼你就能夠在這個子組件內使用 styleName
屬性了。例如:
import React from 'react'; import CSSModules from 'react-css-modules'; import List from './List'; import styles from './table.css'; class CustomList extends React.Component { render () { let itemTemplate; itemTemplate = (name) => { return <li styleName='item-template'>{name}</li>; }; itemTemplate = CSSModules(itemTemplate, this.props.styles); return <List itemTemplate={itemTemplate} />; } } export default CSSModules(CustomList, styles);
/** * @typedef CSSModules~Options * @see {@link https://github.com/gajus/react-css-modules#options} * @property {Boolean} allowMultiple * @property {Boolean} errorWhenNotFound */ /** * @param {Function} Component * @param {Object} defaultStyles CSS Modules class map. * @param {CSSModules~Options} options * @return {Function} */
你須要用 react-css-modules
包裝你的組件,例如:
import React from 'react'; import CSSModules from 'react-css-modules'; import styles from './table.css'; class Table extends React.Component { render () { return <div styleName='table'> <div styleName='row'> <div styleName='cell'>A0</div> <div styleName='cell'>B0</div> </div> </div>; } } export default CSSModules(Table, styles);
就這麼簡單!
顧名思義,react-css-modules
和 ES7 decorators 語法是兼容的。例如:
import React from 'react'; import CSSModules from 'react-css-modules'; import styles from './table.css'; @CSSModules(styles) export default class extends React.Component { render () { return <div styleName='table'> <div styleName='row'> <div styleName='cell'>A0</div> <div styleName='cell'>B0</div> </div> </div>; } }
簡直碉堡了!
這裏有一個用 webpack 構建的示例,你能夠看下 react-css-modules-examples
CSSModules
函數提供了一些選項在第三個參數的位置上。
CSSModules(Component, styles, options);
或者做爲第二個參數:
@CSSModules(styles, options);
allowMultiple
默認:false
。
容許多個樣式模塊名字。
當 false
,如下會引發一個錯誤。
<div styleName='foo bar' />
errorWhenNotFound
默認:true
。
當 styleName
不能匹配到一個未定義的 CSS Module 時將拋出一個錯誤。
Interoperable CSS 和 CSS 預處理器是兼容的。使用預處理器,你只需添加這個預處理器到 loaders
的數組中便可。例如在這個 webpack 的例子中,它就像安裝 sass-loader
同樣簡單,添加 !sass
到 style-loader
加載器查詢的末尾(加載器是從右到左被依次執行的):
{ test: /\.scss$/, loaders: [ 'style', 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 'resolve-url', 'sass' ] }
開啓 CSS Source maps,須要在 css-loader
和 sass-loader
中添加參數 sourceMap
{ test: /\.scss$/, loaders: [ 'style?sourceMap', 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', 'resolve-url', 'sass?sourceMap' ] }
CSS Mosules 促進了類組合模式,也就是說,每個用在組件裏的 CSS Module 應該定義描述一個元素所需的所有屬性。如:
.box { width: 100px; height: 100px; } .empty { composes: box; background: #4CAF50; } .full { composes: box; background: #F44336; }
類組合促進了標記和語義化樣式更好的分離,若是沒有 CSS Modules,這點將難以實現。
由於 CSS Module 中的類名是本地的,容許你使用諸如 'empty' 或 'full' 這些通用的類名,而無需再加上'box-' 前綴,簡直完美!!
想學更多的類組合規則?我建議你讀下 Glen Maddern 的關於 CSS Modules 的文章,還有官方文檔 spec of the CSS Modules。
設想下有這麼個例子:
.box { width: 100px; height: 100px; } .box-empty { background: #4CAF50; } .box-full { background: #F44336; }
<div class='box box-empty'></div>
這是標準的 OOCSS 模式,這種模式最大的問題就是,若是你想改變樣式,你幾乎每次還要修改 HTML。
接下來是一個學習實踐,以加深對類組合本質的理解。CSS Modules 支持一個本機方法,該方法是要組合的 CSS Mosules 使用 composes
關鍵字實現的。CSS 預處理不是必須的。
在 SCSS 中你可使用 @extend
關鍵字,和使用 Mixin Directives去寫組合,例如:
使用 @extend
:
%box { width: 100px; height: 100px; } .box-empty { @extend %box; background: #4CAF50; } .box-full { @extend %box; background: #F44336; }
編譯後,獲得:
.box-empty, .box-full { width: 100px; height: 100px; } .box-empty { background: #4CAF50; } .box-full { background: #F44336; }
使用 mixins:
@mixin box { width: 100px; height: 100px; } .box-empty { @include box; background: #4CAF50; } .box-full { @include box; background: #F44336; }
編譯後,獲得:
.box-empty { width: 100px; height: 100px; background: #4CAF50; } .box-full { width: 100px; height: 100px; background: #F44336; }
CSS Modules 不會限制你使用全局 CSS。用法以下:
:global .foo { }
可是呢,仍是請你謹慎使用全局樣式。在使用 CSS Modules 過程當中,只有少數狀況下才會用到全局樣式,例如:normalization。
避免使用多個 CSS Modules 去描述單個元素。詳見本文檔前面的 類組合 部分介紹。
可是若是你非要使用多個 CSS Modules 去描述一個元素,那麼就開啓 allowMultiple
選項。(參見文檔前面 選項 部分)。當多個 CSS Modules 被用來描述一個元素時,react-css-modules
將會爲每個在 styleName
聲明中匹配的 CSS Module 附加一個獨一無二的類名。如:
.button { } .active { }
<div styleName='button active'></div>
這會把Interoperable CSS 和 CSS class 都映射到目標元素上。