CSS Modules



What

CSS Modules:使用JS管理CSS依赖,学习成本几乎为零,完美搭配 webpack / React 。


Trouble


全局污染

.container {
  .title {
    & > span {

    }
  }
  .content {
    p {

    }
    .side {

    }
  }
}

命名混乱

/*小A*/
.container {}
.container-header {}
.container-footer {}

/*小B*/
.container2 {}
.container2-header {}
.container2-footer {}

less

<section class="abc-container">
  <h1 class="title">this is title</h1>
  <div class="content">
    <p>this is <span class="bar">content</span></p>
  </div>
</section>
.abc-container {
  .title {
    color: red;
    font-size: 14px;
  }
  .content {
    margin: 20px;
    .bar {
      color: blue;
      font-size: 20px;
    }
  }
}

[note] 这是我们现在的less写法,我们使用命名空间来确保样式不会冲突。随着这个页面越来越复杂,就变成了这样: [/note]

[note] 这样,一个 less 就会上百行,后期也并不好维护。其实,我们这么写只是为了解决样式冲突问题而已,而这一点 CSS Modules 早就完美解决了。 [/note]


CSS Modules

import styles from './style.css'

class Demo extends React.Component {
  render() {
    return (
      <section>
        <h1 className={styles.title}>this is title</h1>
        <div className={styles.content}>
          <p>this is <span className={styles.bar}>content</span></p>
        </div>
      </section>
    )
  }
}
/* style.css */
.title {
  color: red;
  font-size: 14px;
}
.content {
  margin: 20px;
}
.bar {
  color: blue;
  font-size: 20px;
}

react渲染后的DOM

[note] 解决了什么问题? 不必再有类似 abc-container 的命名空间了,这个页面和别的页面样式绝不会冲突,哪怕class名字是一样的! [/note]


theory

[note] 为什么绝对不会冲突?现在来介绍一下 CSS Modules 的原理 [/note]


webpack config

webpack 的 css-loaderlocalIdentName 配置项::

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader?modules&localIdentName=[name]__[local]-[hash:base64:5]'
      ]
    }
  ]
}

styles对象

[note] modules:: 开启 CSS Modules localIdentName:: 具体生成规则,name、local在加hash保证绝对不会冲突 [/note]


Use

[note] CSS Module 很简单,几乎是零成本。 [/note]


作用域

.title {
  color: red;
}

:global(.title) {
  color: green;
}

:global {
  .title {}
}

[note] 凡是这样声明的 .title ,都不会被编译成哈希字符串。 [/note]


样式复用

.className {
  color: green;
  background: red;
}

.otherClassName {
  composes: className;
  color: yellow;
}

||

.otherClassName {
  background: red;
  color: yellow;
}

引入其他文件样式

.otherClassName {
  composes: className from "./style.css";
}

简化 composes 路径

{
  loader: 'css-loader',
  options: {
    modules: true,
    localIdentName: '[local]___[hash:base64:5]',
    importLoaders: 1,
    alias: {
      containers: ROOT + '/src/containers',
    }
  }
}
.otherClassName {
  composes: className from '../../../style.css';
}

.otherClassName {
  composes: className from 'containers/A/style.css';
}

以上是 CSS Modules 的所有功能,具体可看官方demo


变量(结合 postcss)

  • postcss-loader
  • postcss-modules-values

  1. 根目录新建 postcss.config.js
module.exports = {
  plugins: [
    require('postcss-modules-values')
  ]
}
  1. webpack.config.js
{
  test: /\.css$/,
  use: [
    'style-loader',
    'css-loader?modules&localIdentName=[name]__[local]-[hash:base64:5]&importLoaders=1',
    'postcss-loader'
  ]
}

importLoaders 必须设置,具体见 css-loader 的文档

[note] 因为 CSS Modules 设计得及其简单,所以一些功能可以用 postcss 实现 [/note]

[note] postcss 等同于 JS 中的 babel,是用标准的CSS语法转译为 CSS less/sass 等同与 JS 中的 coffee,是用自己的语法转译为 CSS [/note]


变量共享 (结合postcss)

@value xxx: #c1c3c4;

.title {
  color: xxx;
}
.content {
  color: xxx;
}
// CSS、JS变量共享,这是 CSS Modules 独有的功能
import styles from './style.css';
console.log(styles.xxx) ; //#c1c3c4

在 React 使用 CSS Modules


import styles from './style.css'

class Demo extends React.Component {
  render() {
    return (
      <section>
        <h1 className={styles.title}>this is title</h1>
        <div className={styles.content}>
          <p>this is <span className={styles.bar}>content</span></p>
        </div>
      </section>
    )
  }
}

为了避免太多的 style.,使用 babel 插件 react-css-modules::

// .babelrc
{
  "plugins": [
    ["react-css-modules",
      {
        "generateScopedName":"[local]___[hash:base64:5]"
      }
    ]
  ]
}

import './style.css'

class Demo extends React.Component {
  render() {
    return (
      <section>
        <h1 styleName="title">this is title</h1>
        <div styleName="content">
          <p>this is <span styleName="bar">content</span></p>
        </div>
      </section>
    )
  }
}

Q & A


THANKS