CSS-Modules初识

52次阅读

共计 4817 个字符,预计需要花费 13 分钟才能阅读完成。

什么是 CSS Modules

套用 CSS Modules 官网的话来说,一个 css 文件代表一个模块,这个 css 文件中的类名(class names)和 动画名(animation names)默认都有一个局部作用域,简单来说就是 css 也有作用域了(其实这里不是很准确,只是利用了名字转换,让 css 像是拥有了作用域一般)。
Just show me the code
原始代码

    /*style.css*/
    .header {
        color: red;
        font-size: 20px;
    }
    /* 或者 */
    :lcoal(.header) {
        color: red;
        font-size: 20px;
    }
    import style from './style.css';
    ...
    render() {
        return <div className="page">
            <section className={style.header}>
                header
            </section>
        </div>
    }

转换之后代码

    .style__header___YHKJLJH {
        color: red;
        font-size: 20px;
    }
    <div class="page">
        <div class="style__header___YHKJLJH">
            header
        </div>
    </div>

为什么要用 CSS Modules

个人觉得主要是 CSS Modules 用于提供 css 局部作用域的能力,让 css 更加可控,避免污染全局样式。以往开发过程中往往只能靠命名规范,已经程序员自觉遵守团队的命名规范来避免样式污染。当项目越来越大,团队成员越来越多,就很难避免出现样式被污染的情况。此时改起样式难免有点束手束脚了,那么如果现在有一种方式(CSS Modules)能让你写的样式完全与其他的样式隔离,是多么爽的事情。

先来了解 css-loader 中与 CSS Modules 相关的配置

CSS Modules 其实并不是官方的功能,而是项目在编译打包阶段来修改类名,替换对应的 class,实质上 webpack 打包时是依赖 css-loader 来进行处理,让 CSS Modules 生效的。

  1. modules: 默认值为 false;true 表示开启 CSS Modules
  2. sourceMap:默认为 false,不开启 sourceMap; true 开启 sourceMap,开发环境下开启比较实用
  3. getLocalIdent:默认为 undefined,用 function 自定义生成的类名
  4. localIdentName:默认 [path]___[name]__[local]___[hash:base64:5],可以自定义了类名的模板,可以进行适当修改

开始实战吧

这里我们就以 reactjs 为例,来开启 CSS Modules 之旅。

这里我们做一个默认的约定,使用.module.css 或者 module.scss 作为文件后缀,区分全局的样式和局部样式。

  • 首先使用 create-react-app 创建一个 my-app 项目, 然后运行项目 (创建过程需要一定时间,因为这里会把依赖包都安装好。)
npx create-react-app my-app

npm run start

演示项目使用的版本信息

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-scripts": "3.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {"extends": "react-app"},
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
  • 不做任何修改即可使用 CSS Modules

在 my-app/src 目录添加一个 style.module.css 文件

  .title {font-size: 25px;}
  import React from 'react';
  import logo from './logo.svg';
  import './App.css';
  import style from './style.module.css';

  function App() {
  return (
      <div className="App">
      <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p className={style.title}>Hello CSS Modules</p>
      </header>
      </div>
  );
  }

  export default App;

保存后查看浏览器,可以在控制台中看到 p 标签的 class=”style_title__2t5Z0″,对应的样式类名 title 也转换成 style_title__2t5Z0。可见使用 create-react-app, 默认已经支持 CSS Modules。

  • 查看 webpack 配置

新版本 create-react-app 创建的项目把打包构建的脚本都放在 npm 包中了,所以需要用 npm run eject 命令解压出相关文件夹, 运行成功后项目目录下会增加两个文件夹 config,scripts,我们主要查看 config/webpack.config.js,直接查看 oneOf 里面的内容中的这一块

  /*css*/
  {
      test: cssModuleRegex,
      use: getStyleLoaders({
      importLoaders: 1,
      sourceMap: isEnvProduction && shouldUseSourceMap,
      modules: true,
      getLocalIdent: getCSSModuleLocalIdent,
      }),
  },
  /*scss*/
  {
      test: sassModuleRegex,
      use: getStyleLoaders(
      {
          importLoaders: 2,
          sourceMap: isEnvProduction && shouldUseSourceMap,
          modules: true,
          getLocalIdent: getCSSModuleLocalIdent,
      },
      'sass-loader'
      ),
  },

  //cssModuleRegex 是匹配 cssModule 的正则,/\.module\.css$/
  //getStyleLoaders 是用于获取对应的 loader,我们只需关注其中的 css-loader,css-loader 使用的 options 就是 getStyleLoaders 的第一个参数,modules: true 打开 css modules,其实 css modules 是由 css-loader 提供支持
  const getStyleLoaders = (cssOptions, preProcessor) => {
      const loaders = [
          ...
          {loader: require.resolve('css-loader'),
              options: cssOptions,
          },
          ...
          return loaders;

  }

可见在 create-react-app 创建的项目中 CSS Modules 已经开箱可用了(支持 css,scss),只需要修改一下使用样式的方式(import), 以及使用 module.css 和 module.scss(sass) 后缀

  • 已有项目中引入 CSS Modules

实际情况中,我们经常是需要在自己搭建的脚手架中引入 CSS Modules,那也是非常简单的事情,只需要修改 css-loader 相关配置即可。sourceMap 只在开发环境下打开,localIdentName 在开发环境下显示比较详细,可以自由配置,而生产环境下直接显示编码后的 hash。推荐使用 module.scss,module.css 作为 CSS Modules 文件的后缀,和已有的样式文件区分处理。

    // 是否开发环境
    const isDevelop = true;
    ...
     {
        test: /\.module\.scss$/,
        use: [
            'style-loader',
            {
                'css-loader',
                options: {
                    sourceMap: isDevelop,
                    modules: true,
                    localIdentName: dev ? '[path]___[name]__[local]___[hash:base64:5]' : '[hash:base64]',
                }
            }
            'postcss-loader',
            'sass-loader'
    },
  • 终极武器 babel-plugin-react-css-modules

现在已经解决了在样式文件中不再需要:local 来标志哪些类名需要进行转换的问题,那么在使用的时候还是有一些问题,CSS Modules 推荐使用驼峰命名,What?
show me code

    import style from './style.module.css';

    <div className={style.pageHeader}></div>

    <div className={style['page-header']}></div>

很明显使用驼峰简洁许多了,官方推荐也不是没有道理的。除此之外,每次写 className 时,都要用 a.b 方式,能不能更简单一些?当然可以:

    import './style.module.css';
    ...
    render() {
        return (<div styleName="title"></div>)
    }

这样是不是感觉简单高效了许多。不过还需要借助一个 babel 插件 babel-plugin-react-css-modules,这个插件会在打包阶段把 styleName 属性的值转换为对应的名字(generateScopedName 定义的格式),之后把 styleName 属性值加到已有 className 中(如果没有 className 则创建)。

    npm install babel-plugin-react-css-modules --save-dev
    // 用于支持 scss
    npm install postcss-scss

需要在 webpack 配置文件的 babel-loader 添加配置如下:

    {test: /\.(js|mjs|jsx|ts|tsx)$/,
        loader: 'babel-loader',
        options: {
            plugins: [
                ...
                [
                    'react-css-modules',
                    {
                        webpackHotModuleReloading: true,
                        autoResolveMultipleImports: true,
                        generateScopedName: '[path]___[name]__[local]___[hash:base64:5]',
                        filetypes: {
                            '.scss': {syntax: 'postcss-scss',}
                        }
                    }
                ]
            ]
        }
    }    

其中 generateScopedName 如果配置,则需要和 style-loader 中的 localIdentName 保持一致,否则会导致 styleName 使用的名字与实际生成的 classname 不一致,样式无效!filetypes 的配置是为了支持 scss,对于 scss 样式会先由 postcss-scss 做一次处理。

总结

以上就是个人关于 CSS Modules 初次探索,并且已经开始在项目中使用 CSS Modules,写起样式来可以随心所欲,再也不用担心样式污染了。

正文完
 0