webpack css_module配置与使用 [转] 使用babel-plugin-react-css-modules简化CSS Modules的使用

https://www.jianshu.com/p/0246794ac0c3

https://segmentfault.com/a/1190000015715538

1.在每个样式中都是style.*中的形式比较麻烦,可以使用react-css-modules解决这种问题。例如在app.js中

2.react-css-modules缺点是是需要运行时的依赖,而且需要在运行时才获取className,性能损耗大。在比较大的项目中,会导致较大的延迟。那么这个问题怎么解决那,可以使用bable-plugins-react-css-modules 把className获取前置到编译阶段。

3.bable-plugins-react-css-modules与react-css-modules是同一个作者开发的工具,bable-plugins-react-css-modules相对于react-css-modules,在性能方面有了较大的提高。

bable-plugins-react-css-modules有两种配置方式,一是配置在webpack.config.js,二是配置在.babelrc中,本文采取的方式是配置在webpack.config.js中。

...
       {
       test:  [/\.js$/, /\.jsx$/, /\.es6$/],
         include: [
           path.resolve(__dirname, 'src'),
         ],
         use: {
           loader: "babel-loader",
           options: {
             cacheDirectory: true,
             plugins: [
               [ "react-css-modules", {
                 context: path.resolve(__dirname, "src"),
                 "generateScopedName": "[path][name]__[local]--[hash:base64:5]"
               }]
             ]
           }
         },
       }
      ...

在我们的产品中,均使用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>;
  }
}

但这里显然存在一些细节上的麻烦:

  1. 引入样式时额外增加了一个styles变量
  2. 需要不断写styles.xxx,重复代码

babel-plugin-react-css-modules插件可以一定程度上缓解这些问题,使代码变为:

import React from 'react';
import './table.css';

export default 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>;
  }
}

难点

  1. 我们的产品使用LESS而非原生的CSS来编写样式
  2. 为了生成的类名更漂亮,我们使用CSS Modules用了一个自定义的getLocalIdent实现
  3. 与webpack的整合可能存在一些难点

解决方案

安装依赖

npm i --save babel-plugin-react-css-modules
npm i --save-dev postcss-less

需要注意的是,babel-plugin-react-css-modules有一个运行时依赖,所以用--save安装比较好。而postcss-less则用于解析LESS的语法

调整构建配置

因为有一个自定义的生成类名的函数,所以原有的.babelrc的JSON格式已经不够了(无法表达函数),因此我们要把.babelrc的配置移到babel-loaderoptions里去

在完成移动后,再向其中添加babel-plugin-react-css-modules这一插件,在这个过程中将生成类名的函数抽象出来:

const generateScopedName = (name, filename) => {
    const hash = hasha(filename + name, {algorithm: 'md5'});
    const basename = path.basename(filename, '.less');
    return `${dashCase(basename)}-${name}-${hash.slice(0, 5)}`;
};

exports.babel = {
    loader: 'babel-loader',
    options: {
        cacheDirectory: true,
        presets: [
            // ...预置集
        ],
        plugins: [
            // ...其它插件
            [
                'react-css-modules',
                {
                    context: path.join(__dirname, '..'),
                    exclude: 'node_modules',
                    filetypes: {
                        '.less': {
                            syntax: 'postcss-less'
                        }
                    },
                    generateScopedName: generateScopedName
                }
            ]
        ]
    }
};

以上文件为webpack/loaders.js,相关的配置基本不用修改,原样使用就行。如果这些代码的位置不同,将其中的context配置修改一下,对应至项目根目录就行。

然后调整一下CSS Modules相关的loader的配置,复用generateScopedName函数:

exports.cssModules = {
    loader: 'css-loader',
    options: {
        sourceMap: development,
        modules: true,
        importLoaders: true,
        camelCase: 'dashes',
        getLocalIdent({resourcePath}, localIdentName, localName) {
            return generateScopedName(localName, resourcePath);
        }
    }
};

修改一些源码

需要特别注意:所有从.js中引用.less的代码,都不可以依赖webpack的resolve.modules配置,只能写相对路径了。即原来写import 'common/less/global.less'要改成import './common/less/global.less'

随后按着喜好将已经用了className的地方慢慢改成styleName就行。

在我们的产品中,均使用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>;
  }
}

但这里显然存在一些细节上的麻烦:

  1. 引入样式时额外增加了一个styles变量
  2. 需要不断写styles.xxx,重复代码

babel-plugin-react-css-modules插件可以一定程度上缓解这些问题,使代码变为:

import React from 'react';
import './table.css';

export default 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>;
  }
}

难点

  1. 我们的产品使用LESS而非原生的CSS来编写样式
  2. 为了生成的类名更漂亮,我们使用CSS Modules用了一个自定义的getLocalIdent实现
  3. 与webpack的整合可能存在一些难点

解决方案

安装依赖

npm i --save babel-plugin-react-css-modules
npm i --save-dev postcss-less

需要注意的是,babel-plugin-react-css-modules有一个运行时依赖,所以用--save安装比较好。而postcss-less则用于解析LESS的语法

调整构建配置

因为有一个自定义的生成类名的函数,所以原有的.babelrc的JSON格式已经不够了(无法表达函数),因此我们要把.babelrc的配置移到babel-loaderoptions里去

在完成移动后,再向其中添加babel-plugin-react-css-modules这一插件,在这个过程中将生成类名的函数抽象出来:

const generateScopedName = (name, filename) => {
    const hash = hasha(filename + name, {algorithm: 'md5'});
    const basename = path.basename(filename, '.less');
    return `${dashCase(basename)}-${name}-${hash.slice(0, 5)}`;
};

exports.babel = {
    loader: 'babel-loader',
    options: {
        cacheDirectory: true,
        presets: [
            // ...预置集
        ],
        plugins: [
            // ...其它插件
            [
                'react-css-modules',
                {
                    context: path.join(__dirname, '..'),
                    exclude: 'node_modules',
                    filetypes: {
                        '.less': {
                            syntax: 'postcss-less'
                        }
                    },
                    generateScopedName: generateScopedName
                }
            ]
        ]
    }
};

以上文件为webpack/loaders.js,相关的配置基本不用修改,原样使用就行。如果这些代码的位置不同,将其中的context配置修改一下,对应至项目根目录就行。

然后调整一下CSS Modules相关的loader的配置,复用generateScopedName函数:

exports.cssModules = {
    loader: 'css-loader',
    options: {
        sourceMap: development,
        modules: true,
        importLoaders: true,
        camelCase: 'dashes',
        getLocalIdent({resourcePath}, localIdentName, localName) {
            return generateScopedName(localName, resourcePath);
        }
    }
};

修改一些源码

需要特别注意:所有从.js中引用.less的代码,都不可以依赖webpack的resolve.modules配置,只能写相对路径了。即原来写import 'common/less/global.less'要改成import './common/less/global.less'

随后按着喜好将已经用了className的地方慢慢改成styleName就行。