工具介绍
在 React 组件中无缝映射类名到 CSS 模块。
# React CSS Modules
React CSS Modules implement automatic mapping of CSS modules. Every CSS class is assigned a local-scoped identifier with a global unique name. CSS Modules enable a modular and reusable CSS!
> ## ⚠️⚠️⚠️ DEPRECATION NOTICE ⚠️⚠️⚠️
>
> If you are considering to use `react-css-modules`, evaluate if [`babel-plugin-react-css-modules`](https://github.com/gajus/babel-plugin-react-css-modules) covers your use case.
> `babel-plugin-react-css-modules` is a lightweight alternative of `react-css-modules`.
>
> `babel-plugin-react-css-modules` is not a drop-in replacement and does not cover all the use cases of `react-css-modules`.
> However, it has a lot smaller performance overhead (0-10% vs +50%; see [Performance](https://github.com/gajus/babel-plugin-react-css-modules#performance)) and a lot smaller size footprint (less than 2kb vs +17kb).
>
> It is easy to get started! See the demo https://github.com/gajus/babel-plugin-react-css-modules/tree/master/demo
- [CSS Modules](#css-modules)
- [webpack `css-loader`](#webpack-css-loader)
- [What's the Problem?](#whats-the-problem)
- [The Implementation](#the-implementation)
- [Usage](#usage)
- [Module Bundler](#module-bundler)
- [webpack](#webpack)
- [Development](#development)
- [Production](#production)
- [Browserify](#browserify)
- [Extending Component Styles](#extending-component-styles)
- [`styles` Property](#styles-property)
- [Loops and Child Components](#loops-and-child-components)
- [Decorator](#decorator)
- [Options](#options)
- [`allowMultiple`](#allowmultiple)
- [`handleNotFoundStyleName`](#handlenotfoundstylename)
- [SASS, SCSS, LESS and other CSS Preprocessors](#sass-scss-less-and-other-css-preprocessors)
- [Enable Sourcemaps](#enable-sourcemaps)
- [Class Composition](#class-composition)
- [What Problems does Class Composition Solve?](#what-problems-does-class-composition-solve)
- [Class Composition Using CSS Preprocessors](#class-composition-using-css-preprocessors)
- [Global CSS](#global-css)
- [Multiple CSS Modules](#multiple-css-modules)
## CSS Modules
[CSS Modules](https://github.com/css-modules/css-modules) are awesome. If you are not familiar with CSS Modules, it is a concept of using a module bundler such as [webpack](http://webpack.github.io/docs/) to load CSS scoped to a particular document. CSS module loader will generate a unique name for each CSS class at the time of loading the CSS document ([Interoperable CSS](https://github.com/css-modules/icss) to be precise). To see CSS Modules in practice, [webpack-demo](https://css-modules.github.io/webpack-demo/).
In the context of React, CSS Modules look like this:
```js
import React from 'react';
import styles from './table.css';
export default class Table extends React.Component {
render () {
return
;
}
}
```
Rendering the component will produce a markup similar to:
```js
```
and a corresponding CSS file that matches those CSS classes.
Awesome!
### webpack `css-loader`
[CSS Modules](https://github.com/css-modules/css-modules) is a specification that can be implemented in multiple ways. `react-css-modules` leverages the existing CSS Modules implementation webpack [css-loader](https://github.com/webpack/css-loader#css-modules).
## What's the Problem?
webpack [css-loader](https://github.com/webpack/css-loader#css-modules) itself has several disadvantages:
* You have to use `camelCase` CSS class names.
* You have to use `styles` object whenever constructing a `className`.
* Mixing CSS Modules and global CSS classes is cumbersome.
* Reference to an undefined CSS Module resolves to `undefined` without a warning.
React CSS Modules component automates loading of CSS Modules using `styleName` property, e.g.
```js
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';
class Table extends React.Component {
render () {
return
;
}
}
export default CSSModules(Table, styles);
```
Using `react-css-modules`:
* You are not forced to use the `camelCase` naming convention.
* You do not need to refer to the `styles` object every time you use a CSS Module.
* There is clear distinction between global CSS and CSS Modules, e.g.
```js
```
* You are warned when `styleName` refers to an undefined CSS Module ([`handleNotFoundStyleName`](#handlenotfoundstylename) option).
* You can enforce use of a single CSS module per `ReactElement` ([`allowMultiple`](#allowmultiple) option).
## The Implementation
`react-css-modules` extends `render` method of the target component. It will use the value of `styleName` to look for CSS Modules in the associated styles object and will append the matching unique CSS class names to the `ReactElement` `className` property value.
[Awesome!](https://twitter.com/intent/retweet?tweet_id=636497036603428864)
## Usage
Setup consists of:
* Setting up a [module bundler](#module-bundler) to load the [Interoperable CSS](https://github.com/css-modules/icss).
* [Decorating](#decorator) your component using `react-css-modules`.
### Module Bundler
#### webpack
##### Development
In development environment, you want to [Enable Sourcemaps](#enable-sourcemaps) and webpack [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement.html) (HMR). [`style-loader`](https://github.com/webpack/style-loader) already supports HMR. Therefore, Hot Module Replacement will work out of the box.
Setup:
* Install [`style-loader`](https://www.npmjs.com/package/style-loader).
* Install [`css-loader`](https://www.npmjs.com/package/css-loader).
* Setup `/\.css$/` loader:
```js
{
test: /\.css$/,
loaders: [
'style-loader?sourceMap',
'css-loader?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
]
}
```
##### Production
In production environment, you want to extract chunks of CSS into a single stylesheet file.
> Advantages:
>
> * Fewer style tags (older IE has a limit)
> * CSS SourceMap (with `devtool: "source-map"` and `css-loader?sourceMap`)
> * CSS requested in parallel
> * CSS cached separate
> * Faster runtime (less code and DOM operations)
>
> Caveats:
>
> * Additional HTTP request
> * Longer compilation time
> * More complex configuration
> * No runtime public path modification
> * No Hot Module Replacement
– [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin)
Setup:
* Install [`style-loader`](https://www.npmjs.com/package/style-loader).
* Install [`css-loader`](https://www.npmjs.com/package/css-loader).
* Use [`extract-text-webpack-plugin`](https://www.npmjs.com/package/extract-text-webpack-plugin) to extract chunks of CSS into a single stylesheet.
* Setup `/\.css$/` loader:
* ExtractTextPlugin v1x:
```js
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
}
```
* ExtractTextPlugin v2x:
```js
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader?modules,localIdentName="[name]-[local]-[hash:base64:6]"'
}),
}
```
* Setup `extract-text-webpack-plugin` plugin:
* ExtractTextPlugin v1x:
```js
new ExtractTextPlugin('app.css', {
allChunks: true
})
```
* ExtractTextPlugin v2x:
```js
new ExtractTextPlugin({
filename: 'app.css',
allChunks: true
})
```
Refer to [webpack-demo](https://github.com/css-modules/webpack-demo) or [react-css-modules-examples](https://github.com/gajus/react-css-modules-examples) for an example of a complete setup.
##### Browserify
Refer to [`css-modulesify`](https://github.com/css-modules/css-modulesify).
### Extending Component Styles
Use `styles` property to overwrite the default component styles.
Explanation using `Table` component:
```js
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';
class Table extends React.Component {
render () {
return
;
}
}
export default CSSModules(Table, styles);
```
In this example, `CSSModules` is used to decorate `Table` component using `./table.css` CSS Modules. When `Table` component is rendered, it will use the properties of the `styles` object to construct `className` values.
Using `styles` property you can overwrite the default component `styles` object, e.g.
```js
import customStyles from './table-custom-styles.css';
;
```
[Interoperable CSS](https://github.com/css-modules/icss) can [extend other ICSS](https://github.com/css-modules/css-modules#dependencies). Use this feature to extend default styles, e.g.
```css
/* 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;
}
```
In this example, `table-custom-styles.css` selectively extends `table.css` (the default styles of `Table` component).
Refer to the [`UsingStylesProperty` example](https://github.com/gajus/react-css-modules-examples/tree/master/src/UsingStylesProperty) for an example of a working implementation.
### `styles` Property
Decorated components inherit `styles` property that describes the mapping between CSS modules and CSS classes.
```js
class extends React.Component {
render () {
;
}
}
```
In the above example, `styleName='foo'` and `className={this.props.styles.foo}` are equivalent.
`styles` property is designed to enable component decoration of [Loops and Child Components](#loops-and-child-components).
### Loops and Child Components
`styleName` cannot be used to define styles of a `ReactElement` that will be generated by another component, e.g.
```js
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
{name};
};
return ;
}
}
export default CSSModules(CustomList, styles);
```
The above example will not work. `CSSModules` is used to decorate `CustomList` component. However, it is the `List` component that will render `itemTemplate`.
For that purpose, the decorated component inherits [`styles` property](#styles-property) that you can use just as a regular CSS Modules object. The earlier example can be therefore rewritten to:
```js
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 {name};
};
return ;
}
}
export default CSSModules(CustomList, styles);
```
You can use `styleName` property within the child component if you decorate the child component using `CSSModules` before passing it to the rendering component, e.g.
```js
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 {name};
};