使用CSS Module避免样式污染

2020/9/24 React优化

🌙 使用CSS Module避免样式污染

背景:React老项目没有使用【CRA】 (opens new window)创建,使用的自定义的webpack,样式都是采用普通CSS方式,导致很容易造成全局样式污染。

需求:在兼容老页面的基础上,新的页面采用css module (opens new window)方式避免样式污染。

🌙 1.React中引入CSS方式

🌙 1.1 一般方式

APP.tsx:


 



 





import React from 'react';
import './App.scss';

function App() {
  return (
    <div className="container" />
  );
}

export default App;
1
2
3
4
5
6
7
8
9
10

App.scss:

.container {
    color: red;
    text-align: center;
}
1
2
3
4

🌙 1.2 使用CSS Module

APP.tsx:



 




 





import React from 'react';
// 导入CSS Module定义的CSS对象
import styles from './App.module.scss';

function App() {
  return (
    {/*使用CSS Module属性*/}
    <div className={styles.container} />
  );
}

export default App;
1
2
3
4
5
6
7
8
9
10
11
12

App.module.scss:

.container {
    color: red;
    text-align: center;
}
1
2
3
4

编译后显示:

<div class="container--1g20Q"></div>
1

上面代码中,我们将样式文件App.module.scss输入到style对象,然后当做变量的方式引用style.container作为class类名。

🌙 2.CSS Module原理

CSS Module配置极其简单,其原理简单:通过将CSS文件以模块的形式引入(类似于js中的对象),将CSS

中定义的类名当作变量赋值给jsx即可。

当webpack解析时,会将CSS Module的类名计算或拼接一个唯一的hash值,从而避免样式的污染。

🌙 3.配置webpack

webpack解析less或sass (opens new window)

【Webpack4】CSS 配置之 postcss-loader (opens new window)

webpack.config.js

const cssLoaders = [
  "style-loader", 
  "css-loader",
  { 
      loader: "postcss-loader",
      options: [ 
          require('autoprefixer')
      ]
  },
  "sass-loader",
];
// CSS Module
const cssModuleLoaders = [
  "style-loader",
  {
    loader: "css-loader",
    options: {
      modules: {
        // class命名规则
        /**
         * localIdentName格式说明
         * path: 路径
         * name: css文件名
         * local: 类名
         * hash: 16位字符串
         '[path][name]__[local]--[hash:base64:5]'
         .src-styles-main__world-grid--R7u-K
         ---------------  ----------  -----
         path,name     local      hash
         * */
        mode: "local",
        localIdentName: "[name]__[local]--[hash:base64:5]",
      },
    },
  },
  "postcss-loader",
  "sass-loader",
];

module.exports = {
  entry: './src/index.tsx',
  mode: devMode ? 'development' : 'production',
  devtool: devMode ? 'cheap-source-map' : 'hidden-source-map',
  output: {
    // 开发模式不用hash
    filename: devMode ? 'js/[name].js' : 'js/[name].[chunkhash:8].js',
    path: config.output,
    publicPath: config.publicPath
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  },
  module: {
    rules: [
      // 支持 sass
      {
        test: /\.module\.(scss|sass)$/, use: cssModuleLoaders,
      },
      // 普通方式,不使用css module
      {
        test: /\.scss$/, exclude: /\.module\.(scss|sass)$/, use: cssLoaders,
      },  
      // 支持 css
      {
        test: /\.css$/, use: cssLoaders,
      }, 
    ]
  },
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

Loaders解析顺序是从后向前,解析顺序:

  • sass-loader:解析sass|scss为css。
  • postcss-loader:添加浏览器前缀,压缩 CSS等。
  • css-loader:用于加载css文件,并且转换成commonjs对象。
  • style-loader:将样式通过<style>标签插入到head中。

注意:{test: /\.scss$/, exclude: /\.module\.(scss|sass)$/, use: cssLoaders},(注意顺序,放在后面)由于之前的老页面使用了普通的方式,我们还得保持原来的方式可用,这里进行了剔除。

🌙 3.解决ts报错

配置完上述步骤,如果不出意料的话,会爆出TS2307: Cannot find module './index.module.scss'.的错误:

在项目中增加css.module.d.ts声名:

declare module "*.module.css" {
  const classes: { [key: string]: string };
  export default classes;
}

declare module "*.module.scss" {
  const classes: { [key: string]: string };
  export default classes;
}

declare module "*.module.sass" {
  const classes: { [key: string]: string };
  export default classes;
}

declare module "*.module.less" {
  const classes: { [key: string]: string };
  export default classes;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

🌙 4.CRA使用CSS Module

添加 CSS Modules 样式表 (opens new window)

🌙 5.踩坑记录

由于项目管理不规范,之前新入职的同学,不熟悉项目,随意命名,比如:使用普通的css方式引入时,有一个文件被命名为a.module.scss,然后在tsx文件里面使用impoort './a.module.scss'

在未使用css module之前是正常的,但是引入css module之后发现style没有加载出来,这时就需要去一一排查了,然后把文件命名为a.scss即可。

不过,一般这种很少出现吧。