React CSS Modules(譯文)

原文地址:react-css-modules
閱讀本文前建議了解 CSS Modules 的知識(shí)艺谆。墻裂推薦閱讀 Cam 的文章 CSS Modules詳解及React中實(shí)踐

React CSS Modules 實(shí)現(xiàn)了自動(dòng)化映射 CSS modules榨惰。每個(gè) CSS 類都被賦予了一個(gè)帶有全局唯一名字的本地標(biāo)識(shí)符。 CSS Modules 實(shí)現(xiàn)了模塊化和復(fù)用性静汤。

CSS Modules

CSS Mosules 碉堡了琅催。如果你對(duì) CSS Modules 還不夠熟悉,那么沒關(guān)系虫给,它只是一個(gè)使用 webpack 之類的模塊打包機(jī)加載 CSS 作用于特定文檔的概念藤抡。CSS module loader 將為每一個(gè) CSS 類在加載 CSS 文檔(確切的說,這個(gè)文檔就是Interoperable CSS)的時(shí)候生成一個(gè)唯一的名字抹估。你可以來看下這個(gè) CSS Modules 實(shí)踐例子——webpack-demo缠黍。

在React語法環(huán)境中,CSS Modules 看起來是這樣子的:

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>;

組件渲染出來后會(huì)生成類似于這樣的一個(gè)標(biāo)記:

<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>

同時(shí)也會(huì)生成對(duì)應(yīng)的匹配那些CSS類的CSS文件药蜻,是不是碉堡了瓷式?!

webpack css-loader

CSS Modules 是一個(gè)可以被多種方法實(shí)現(xiàn)的規(guī)范语泽。react-css-modules 利用 webpack css-loader 所提供的功能啟用了現(xiàn)有的 CSS Modules 的集成贸典。

現(xiàn)有的問題

webpack 的 css-loader 本身有幾處劣勢:

  • 你必須使用駝峰式類名。
  • 無論何時(shí)構(gòu)建一個(gè) className 你都必須使用 style 對(duì)象踱卵。
  • 混合類模塊以及全局 CSS 類不夠靈活瓤漏。
  • 引用一個(gè)未定義的 CSS 模塊時(shí)解析結(jié)果為 undefines ,但并無相關(guān)警告提示。

React CSS Modules 組件自動(dòng)加載應(yīng)用了 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 好處多多:

  • 你不用再被強(qiáng)制使用駝峰式命名規(guī)則

  • 你不必每次使用一個(gè) CSS 模塊時(shí)還要引用 styles 對(duì)象蔬充。

  • 有一個(gè)明顯的區(qū)別在全局 CSS 和 CSS Modules 之間,示例如下:

    <div className='global-css' styleName='local-module'></div>
    
  • 當(dāng) styleName 引用一個(gè)為定義的 CSS Module 時(shí)班利,你會(huì)得到一個(gè)警告信息饥漫。(errorWhenNotFound 選項(xiàng))

  • 你可以為每一個(gè) ReactElement 只使用單獨(dú)的 CSS Module。(allowMultiple 選項(xiàng))罗标。

實(shí)現(xiàn)

react-css-modules 擴(kuò)展了目標(biāo)組件的 render 方法庸队。它將根據(jù) styleName 的值在關(guān)聯(lián)的 style 對(duì)象中查找對(duì)應(yīng)的 CSS Modules,并為 ReactElement className 屬性值添加相匹配的獨(dú)一無二的 CSS 類名闯割。

碉堡了彻消!

你可以參照下這個(gè)例子進(jìn)一步加深印象,react-css-modules-examples宙拉。

用法

設(shè)置包括:

  • 設(shè)置一個(gè) module bundler 加載 Interoperable CSS宾尚。
  • 使用 react-css-modules 修整你的組件。

如何設(shè)置一個(gè) module bundler呢谢澈?

webpack

開發(fā)模式

開發(fā)環(huán)境下煌贴,若你想啟用 Sourcemaps,并要使用 webpack 的 Hot Module Replacement (HMR锥忿,熱替換)牛郑。style-loader 已經(jīng)支持 HMR。因此敬鬓,Hot Module Replacement 開箱即用淹朋。

設(shè)置步驟:

  • 安裝 style-loader
  • 安裝 css-loader钉答。
  • 設(shè)置 /\.css$/ 加載器础芍,如下:
{
    test: /\.css$/,
    loaders: [
        'style?sourceMap',
        'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
    ]
}

生產(chǎn)模式

在生產(chǎn)環(huán)境中,如果你想把CSS單獨(dú)提取出來的話希痴,你需要了解這樣做的好處和壞處者甲。

優(yōu)點(diǎn):

  • 更少的樣式標(biāo)簽
  • CSS SourceMap
  • CSS 并行請求
  • CSS 緩存分離
  • 頁面渲染更快(更少的代碼以及更少的 DOM 操作)

缺點(diǎn):

  • 額外的 HTTP 請求
  • 較長的編譯時(shí)間
  • 較復(fù)雜的配置
  • 不支持變更運(yùn)行環(huán)境公共路徑
  • 不支持 Hot Module Replacement

extract-text-webpack-plugin

設(shè)置步驟:

  • 安裝 style-loader

  • 安裝 css-loader

  • 使用 extract-text-webpack-plugin 提取 CSS 到一個(gè)單獨(dú)的樣式表。

  • 設(shè)置 /\.css$/ 加載器:

    {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
    }
    
  • 設(shè)置 extract-text-webpack-plugin 插件:

    new ExtractTextPlugin('app.css', {
        allChunks: true
    })
    

完整實(shí)例請參照 webpack-demo 或者 react-css-modules-examples砌创。

Browserify(如果你是使用這個(gè)構(gòu)建工具的話)

請參考 css-modulesify虏缸。

擴(kuò)展組件樣式

使用 styles 屬性重寫默認(rèn)的組件樣式。

示例如下:

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);

在這個(gè)例子中嫩实,CSSModules 被用來美化 Table 組件通過引用 ./table.css CSS 模塊刽辙。當(dāng) Table 組件渲染完畢,它將使用 styles 對(duì)象的屬性構(gòu)建 className 的值甲献。

使用 styles 屬性你可以覆蓋組件默認(rèn)的 styles 對(duì)象宰缤。例如:

import customStyles from './table-custom-styles.css';

<Table styles={customStyles} />;

Interoperable CSS 可以擴(kuò)展其他 ICSS。利用這個(gè)功能可以擴(kuò)展默認(rèn)樣式,例如:

/* 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;
}

在這個(gè)例子中慨灭,table-custom-styles.css 有選擇的擴(kuò)展了 table.css (Table 組件的默認(rèn)樣式)朦乏。
這里是一個(gè)更直觀的實(shí)踐例子:UsingStylesProperty example`

style 屬性

包裝過的組件繼承了 styles 屬性氧骤,該屬性描述了 CSS 模塊和 CSS 類之間的映射關(guān)系呻疹。

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} 是等價(jià)的筹陵。
styles 屬性是為 Loops and Child Components 實(shí)現(xiàn)組件包裝而特意設(shè)計(jì)的刽锤!

Loops and Child Components

styleName 不能去定義一個(gè)由其他組件生成的 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);

上面的實(shí)例將不會(huì)工作朦佩。CSSModules 被用來包裝 CustomList 組件并思。然而,它是呈現(xiàn) itemTemplage 的列表組件语稠。

為了解決這個(gè)問題宋彼,包裝過的組件繼承了樣式屬性,這樣你可以將它作為一個(gè)常規(guī)的 CSS 模塊對(duì)象來使用颅筋。
因此宙暇,前面的例子可以改寫為這樣:

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 包裝這個(gè)子組件议泵,那么你就可以在這個(gè)子組件內(nèi)使用 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-modulesES7 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>;
    }
}

簡直碉堡了碉京!

這里有一個(gè)用 webpack 構(gòu)建的示例厢汹,你可以看下 react-css-modules-examples

Options

CSSModules 函數(shù)提供了一些選項(xiàng)在第三個(gè)參數(shù)的位置上。

CSSModules(Component, styles, options);

或者作為第二個(gè)參數(shù):

@CSSModules(styles, options);

allowMultiple

默認(rèn):false谐宙。
允許多個(gè)樣式模塊名字烫葬。
當(dāng) false,以下會(huì)引起一個(gè)錯(cuò)誤凡蜻。

<div styleName='foo bar' />

errorWhenNotFound

默認(rèn):true搭综。
當(dāng) styleName 不能匹配到一個(gè)未定義的 CSS Module 時(shí)將拋出一個(gè)錯(cuò)誤。

SASS, SCSS, LESS 以及其他 CSS 預(yù)處理器

Interoperable CSS 和 CSS 預(yù)處理器是兼容的划栓。使用預(yù)處理器兑巾,你只需添加這個(gè)預(yù)處理器到 loaders 的數(shù)組中即可。例如在這個(gè) webpack 的例子中忠荞,它就像安裝 sass-loader 一樣簡單蒋歌,添加 !sassstyle-loader 加載器查詢的末尾(加載器是從右到左被依次執(zhí)行的):

{
    test: /\.scss$/,
    loaders: [
        'style',
        'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
        'resolve-url',
        'sass'
    ]
}

開啟 Sourcemaps

開啟 CSS Source maps帅掘,需要在 css-loadersass-loader 中添加參數(shù) sourceMap

{
    test: /\.scss$/,
    loaders: [
        'style?sourceMap',
        'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
        'resolve-url',
        'sass?sourceMap'
    ]
}

類組合

CSS Mosules 促進(jìn)了類組合模式,也就是說堂油,每一個(gè)用在組件里的 CSS Module 應(yīng)該定義描述一個(gè)元素所需的全部屬性修档。如:

.box {
    width: 100px;
    height: 100px;
}

.empty {
    composes: box;

    background: #4CAF50;
}

.full {
    composes: box;

    background: #F44336;
}

類組合促進(jìn)了標(biāo)記和語義化樣式更好的分離,如果沒有 CSS Modules称诗,這點(diǎn)將難以實(shí)現(xiàn)萍悴。

因?yàn)?CSS Module 中的類名是本地的,允許你使用諸如 'empty' 或 'full' 這些通用的類名寓免,而無需再加上'box-' 前綴,簡直完美<莆袜香!

想學(xué)更多的類組合規(guī)則?我建議你讀下 Glen Maddern 的關(guān)于 CSS Modules 的文章鲫惶,還有官方文檔 spec of the CSS Modules蜈首。

類組合解決了什么問題?

設(shè)想下有這么個(gè)例子:

.box {
    width: 100px;
    height: 100px;
}

.box-empty {
    background: #4CAF50;
}

.box-full {
    background: #F44336;
}
<div class='box box-empty'></div>

這是標(biāo)準(zhǔn)的 OOCSS 模式欠母,這種模式最大的問題就是欢策,如果你想改變樣式,你幾乎每次還要修改 HTML赏淌。

類組合也可以使用 CSS 預(yù)處理器

接下來是一個(gè)學(xué)習(xí)實(shí)踐踩寇,以加深對(duì)類組合本質(zhì)的理解。CSS Modules 支持一個(gè)本機(jī)方法六水,該方法是要組合的 CSS Mosules 使用 composes 關(guān)鍵字實(shí)現(xiàn)的俺孙。CSS 預(yù)處理不是必須的。

在 SCSS 中你可以使用 @extend 關(guān)鍵字掷贾,和使用 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 不會(huì)限制你使用全局 CSS场靴。用法如下:

:global .foo {

}

但是呢,還是請你謹(jǐn)慎使用全局樣式港准。在使用 CSS Modules 過程中旨剥,只有少數(shù)情況下才會(huì)用到全局樣式,例如:normalization叉趣。

多個(gè) CSS Modules

避免使用多個(gè) CSS Modules 去描述單個(gè)元素泞边。詳見本文檔前面的 類組合 部分介紹。

但是如果你非要使用多個(gè) CSS Modules 去描述一個(gè)元素疗杉,那么就開啟 allowMultiple 選項(xiàng)阵谚。(參見文檔前面 選項(xiàng) 部分)蚕礼。當(dāng)多個(gè) CSS Modules 被用來描述一個(gè)元素時(shí),react-css-modules 將會(huì)為每一個(gè)在 styleName 聲明中匹配的 CSS Module 附加一個(gè)獨(dú)一無二的類名梢什。如:


.button {

}

.active {

}

<div styleName='button active'></div>

這會(huì)把Interoperable CSS 和 CSS class 都映射到目標(biāo)元素上奠蹬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嗡午,隨后出現(xiàn)的幾起案子囤躁,更是在濱河造成了極大的恐慌,老刑警劉巖荔睹,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狸演,死亡現(xiàn)場離奇詭異,居然都是意外死亡僻他,警方通過查閱死者的電腦和手機(jī)宵距,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吨拗,“玉大人满哪,你說我怎么就攤上這事∪芭瘢” “怎么了哨鸭?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長娇妓。 經(jīng)常有香客問我像鸡,道長,這世上最難降的妖魔是什么峡蟋? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任坟桅,我火速辦了婚禮,結(jié)果婚禮上蕊蝗,老公的妹妹穿的比我還像新娘仅乓。我一直安慰自己,他們只是感情好蓬戚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布夸楣。 她就那樣靜靜地躺著,像睡著了一般子漩。 火紅的嫁衣襯著肌膚如雪豫喧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天幢泼,我揣著相機(jī)與錄音紧显,去河邊找鬼。 笑死缕棵,一個(gè)胖子當(dāng)著我的面吹牛孵班,可吹牛的內(nèi)容都是我干的涉兽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼篙程,長吁一口氣:“原來是場噩夢啊……” “哼枷畏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起虱饿,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤拥诡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后氮发,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渴肉,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年爽冕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宾娜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扇售,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嚣艇,到底是詐尸還是另有隱情承冰,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布食零,位于F島的核電站困乒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贰谣。R本人自食惡果不足惜娜搂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吱抚。 院中可真熱鬧百宇,春花似錦、人聲如沸秘豹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽既绕。三九已至啄刹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凄贩,已是汗流浹背誓军。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疲扎,地道東北人昵时。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓捷雕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親债查。 傳聞我的和親對(duì)象是個(gè)殘疾皇子非区,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容