Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS Modules 详解及 React 中实践 #5

Open
camsong opened this issue Dec 31, 2015 · 53 comments
Open

CSS Modules 详解及 React 中实践 #5

camsong opened this issue Dec 31, 2015 · 53 comments

Comments

@camsong
Copy link
Owner

camsong commented Dec 31, 2015

CSS Modules

CSS 是前端领域中进化最慢的一块。由于 ES2015/2016 的快速普及和 Babel/Webpack 等工具的迅猛发展,CSS 被远远甩在了后面,逐渐成为大型项目工程化的痛点。也变成了前端走向彻底模块化前必须解决的难题。

CSS 模块化的解决方案有很多,但主要有两类。一类是彻底抛弃 CSS,使用 JS 或 JSON 来写样式。Radiumjsxstylereact-style 属于这一类。优点是能给 CSS 提供 JS 同样强大的模块化能力;缺点是不能利用成熟的 CSS 预处理器(或后处理器) Sass/Less/PostCSS,:hover:active 伪类处理起来复杂。另一类是依旧使用 CSS,但使用 JS 来管理样式依赖,代表是 CSS Modules。CSS Modules 能最大化地结合现有 CSS 生态和 JS 模块化能力,API 简洁到几乎零学习成本。发布时依旧编译出单独的 JS 和 CSS。它并不依赖于 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。是我认为目前最好的 CSS 模块化解决方案。近期在项目中大量使用,下面具体分享下实践中的细节和想法。

CSS 模块化遇到了哪些问题?

CSS 模块化重要的是要解决好两个问题:CSS 样式的导入和导出。灵活按需导入以便复用代码;导出时要能够隐藏内部作用域,以免造成全局污染。Sass/Less/PostCSS 等前仆后继试图解决 CSS 编程能力弱的问题,结果它们做的也确实优秀,但这并没有解决模块化最重要的问题。Facebook 工程师 Vjeux 首先抛出了 React 开发中遇到的一系列 CSS 相关问题。加上我个人的看法,总结如下:

  1. 全局污染
    CSS 使用全局选择器机制来设置样式,优点是方便重写样式。缺点是所有的样式都是全局生效,样式可能被错误覆盖,因此产生了非常丑陋的 !important,甚至 inline !important 和复杂的选择器权重计数表,提高犯错概率和使用成本。Web Components 标准中的 Shadow DOM 能彻底解决这个问题,但它的做法有点极端,样式彻底局部化,造成外部无法重写样式,损失了灵活性。
  2. 命名混乱
    由于全局污染的问题,多人协同开发时为了避免样式冲突,选择器越来越复杂,容易形成不同的命名风格,很难统一。样式变多后,命名将更加混乱。
  3. 依赖管理不彻底
    组件应该相互独立,引入一个组件时,应该只引入它所需要的 CSS 样式。但现在的做法是除了要引入 JS,还要再引入它的 CSS,而且 Saas/Less 很难实现对每个组件都编译出单独的 CSS,引入所有模块的 CSS 又造成浪费。JS 的模块化已经非常成熟,如果能让 JS 来管理 CSS 依赖是很好的解决办法。Webpack 的 css-loader 提供了这种能力。
  4. 无法共享变量
    复杂组件要使用 JS 和 CSS 来共同处理样式,就会造成有些变量在 JS 和 CSS 中冗余,Sass/PostCSS/CSS 等都不提供跨 JS 和 CSS 共享变量这种能力。
  5. 代码压缩不彻底
    由于移动端网络的不确定性,现在对 CSS 压缩已经到了变态的程度。很多压缩工具为了节省一个字节会把 '16px' 转成 '1pc'。但对非常长的 class 名却无能为力,力没有用到刀刃上。

上面的问题如果只凭 CSS 自身是无法解决的,如果是通过 JS 来管理 CSS 就很好解决,因此 Vjuex 给出的解决方案是完全的 CSS in JS,但这相当于完全抛弃 CSS,在 JS 中以 Object 语法来写 CSS,估计刚看到的小伙伴都受惊了。直到出现了 CSS Modules。

CSS Modules 模块化方案

CSS Modules Logo

CSS Modules 内部通过 ICSS 来解决样式导入和导出这两个问题。分别对应 :import:export 两个新增的伪类。

:import("path/to/dep.css") {
  localAlias: keyFromDep;
  /* ... */
}
:export {
  exportedKey: exportedValue;
  /* ... */
}

但直接使用这两个关键字编程太麻烦,实际项目中很少会直接使用它们,我们需要的是用 JS 来管理 CSS 的能力。结合 Webpack 的 css-loader 后,就可以在 CSS 中定义样式,在 JS 中导入。

启用 CSS Modules

// webpack.config.js
css?modules&localIdentName=[name]__[local]-[hash:base64:5]

加上 modules 即为启用,localIdentName 是设置生成样式的命名规则。

/* components/Button.css */
.normal { /* normal 相关的所有样式 */ }
.disabled { /* disabled 相关的所有样式 */ }
/* components/Button.js */
import styles from './Button.css';

console.log(styles);

buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

生成的 HTML 是

<button class="button--normal-abc53">Submit</button>

注意到 button--normal-abc53 是 CSS Modules 按照 localIdentName 自动生成的 class 名。其中的 abc53 是按照给定算法生成的序列码。经过这样混淆处理后,class 名基本就是唯一的,大大降低了项目中样式覆盖的几率。同时在生产环境下修改规则,生成更短的 class 名,可以提高 CSS 的压缩率。

上例中 console 打印的结果是:

Object {
  normal: 'button--normal-abc53',
  disabled: 'button--disabled-def884',
}

CSS Modules 对 CSS 中的 class 名都做了处理,使用对象来保存原 class 和混淆后 class 的对应关系。

通过这些简单的处理,CSS Modules 实现了以下几点:

  • 所有样式都是 local 的,解决了命名冲突和全局污染问题
  • class 名生成规则配置灵活,可以此来压缩 class 名
  • 只需引用组件的 JS 就能搞定组件所有的 JS 和 CSS
  • 依然是 CSS,几乎 0 学习成本

样式默认局部

使用了 CSS Modules 后,就相当于给每个 class 名外加加了一个 :local,以此来实现样式的局部化,如果你想切换到全局模式,使用对应的 :global

.normal {
  color: green;
}

/* 以上与下面等价 */
:local(.normal) {
  color: green; 
}

/* 定义全局样式 */
:global(.btn) {
  color: red;
}

/* 定义多个全局样式 */
:global {
  .link {
    color: green;
  }
  .box {
    color: yellow;
  }
}

Compose 来组合样式

对于样式复用,CSS Modules 只提供了唯一的方式来处理:composes 组合

/* components/Button.css */
.base { /* 所有通用的样式 */ }

.normal {
  composes: base;
  /* normal 其它样式 */
}

.disabled {
  composes: base;
  /* disabled 其它样式 */
}
import styles from './Button.css';

buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

生成的 HTML 变为

<button class="button--base-daf62 button--normal-abc53">Submit</button>

由于在 .normal 中 composes 了 .base,编译后会 normal 会变成两个 class。

composes 还可以组合外部文件中的样式。

/* settings.css */
.primary-color {
  color: #f40;
}

/* components/Button.css */
.base { /* 所有通用的样式 */ }

.primary {
  composes: base;
  composes: primary-color from './settings.css';
  /* primary 其它样式 */
}

对于大多数项目,有了 composes 后已经不再需要 Sass/Less/PostCSS。但如果你想用的话,由于 composes 不是标准的 CSS 语法,编译时会报错。就只能使用预处理器自己的语法来做样式复用了。

class 命名技巧

CSS Modules 的命名规范是从 BEM 扩展而来。BEM 把样式名分为 3 个级别,分别是:

  • Block:对应模块名,如 Dialog
  • Element:对应模块中的节点名 Confirm Button
  • Modifier:对应节点相关的状态,如 disabled、highlight

综上,BEM 最终得到的 class 名为 dialog__confirm-button--highlight。使用双符号 __-- 是为了和区块内单词间的分隔符区分开来。虽然看起来有点奇怪,但 BEM 被非常多的大型项目和团队采用。我们实践下来也很认可这种命名方法。

CSS Modules 中 CSS 文件名恰好对应 Block 名,只需要再考虑 Element 和 Modifier。BEM 对应到 CSS Modules 的做法是:

/* .dialog.css */
.ConfirmButton--disabled {
}

你也可以不遵循完整的命名规范,使用 camelCase 的写法把 Block 和 Modifier 放到一起:

/* .dialog.css */
.disabledConfirmButton {
}

如何实现CSS,JS变量共享

注:CSS Modules 中没有变量的概念,这里的 CSS 变量指的是 Sass 中的变量。

上面提到的 :export 关键字可以把 CSS 中的 变量输出到 JS 中。下面演示如何在 JS 中读取 Sass 变量:

/* config.scss */
$primary-color: #f40;

:export {
  primaryColor: $primary-color;
}
/* app.js */
import style from 'config.scss';

// 会输出 #F40
console.log(style.primaryColor);

CSS Modules 使用技巧

CSS Modules 是对现有的 CSS 做减法。为了追求简单可控,作者建议遵循如下原则:

  • 不使用选择器,只使用 class 名来定义样式
  • 不层叠多个 class,只使用一个 class 把所有样式定义好
  • 所有样式通过 composes 组合来实现复用
  • 不嵌套

上面两条原则相当于削弱了样式中最灵活的部分,初使用者很难接受。第一条实践起来难度不大,但第二条如果模块状态过多时,class 数量将成倍上升。

一定要知道,上面之所以称为建议,是因为 CSS Modules 并不强制你一定要这么做。听起来有些矛盾,由于多数 CSS 项目存在深厚的历史遗留问题,过多的限制就意味着增加迁移成本和与外部合作的成本。初期使用中肯定需要一些折衷。幸运的是,CSS Modules 这点做的很好:

如果我对一个元素使用多个 class 呢?

没问题,样式照样生效。

如何我在一个 style 文件中使用同名 class 呢?

没问题,这些同名 class 编译后虽然可能是随机码,但仍是同名的。

如果我在 style 文件中使用伪类,标签选择器等呢?
没问题,所有这些选择器将不被转换,原封不动的出现在编译后的 css 中。也就是说 CSS Modules 只会转换 class 名和 id 选择器名相关的样式。

但注意,上面 3 个“如果”尽量不要发生。

CSS Modules 结合 React 实践

className 处直接使用 css 中 class 名即可。

/* dialog.css */
.root {}
.confirm {}
.disabledConfirm {}
import classNames from 'classnames';
import styles from './dialog.css';

export default class Dialog extends React.Component {
  render() {
    const cx = classNames({
      [styles.confirm]: !this.state.disabled,
      [styles.disabledConfirm]: this.state.disabled
    });

    return <div className={styles.root}>
      <a className={cx}>Confirm</a>
      ...
    </div>
  }
}

注意,一般把组件最外层节点对应的 class 名称为 root。这里使用了 classnames 库来操作 class 名。
如果你不想频繁的输入 styles.**,可以试一下 react-css-modules,它通过高阶函数的形式来避免重复输入 styles.**

CSS Modules 结合历史遗留项目实践

好的技术方案除了功能强大炫酷,还要能做到现有项目能平滑迁移。CSS Modules 在这一点上表现的非常灵活。

外部如何覆盖局部样式

当生成混淆的 class 名后,可以解决命名冲突,但因为无法预知最终 class 名,不能通过一般选择器覆盖。我们现在项目中的实践是可以给组件关键节点加上 data-role 属性,然后通过属性选择器来覆盖样式。

// dialog.js
  return <div className={styles.root} data-role='dialog-root'>
      <a className={styles.disabledConfirm} data-role='dialog-confirm-btn'>Confirm</a>
      ...
  </div>
// dialog.css
[data-role="dialog-root"] {
  // override style
}

因为 CSS Modules 只会转变类选择器,所以这里的属性选择器不需要添加 :global

如何与全局样式共存

前端项目不可避免会引入 normalize.css 或其它一类全局 css 文件。使用 Webpack 可以让全局样式和 CSS Modules 的局部样式和谐共存。下面是我们项目中使用的 webpack 部分配置代码:

module: {
  loaders: [{
    test: /\.jsx?$/,
    loader: 'babel'
  }, {
    test: /\.scss$/,
    exclude: path.resolve(__dirname, 'src/styles'),
    loader: 'style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true'
  }, {
    test: /\.scss$/,
    include: path.resolve(__dirname, 'src/styles'),
    loader: 'style!css!sass?sourceMap=true'
  }]
}
/* src/app.js */
import './styles/app.scss';
import Component from './view/Component'

/* src/views/Component.js */
// 以下为组件相关样式
import './Component.scss';

目录结构如下:

src
├── app.js
├── styles
│   ├── app.scss
│   └── normalize.scss
└── views
    ├── Component.js
    └── Component.scss

这样所有全局的样式都放到 src/styles/app.scss 中引入就可以了。其它所有目录包括 src/views 中的样式都是局部的。

总结

CSS Modules 很好的解决了 CSS 目前面临的模块化难题。支持与 Sass/Less/PostCSS 等搭配使用,能充分利用现有技术积累。同时也能和全局样式灵活搭配,便于项目中逐步迁移至 CSS Modules。CSS Modules 的实现也属轻量级,未来有标准解决方案后可以低成本迁移。如果你的产品中正好遇到类似问题,非常值得一试。

@camsong camsong changed the title WIP CSS Modules 详解及 React 中实践 Jan 13, 2016
@windsome
Copy link

windsome commented Jun 12, 2016

very good!我要使用bootstrap-sass版本,如何结合css-loader使用呢?是将bootstrap作为一个单独的全局css,还是单独引用使用到的scss文件?
@camsong 有此类例子吗?

@minwe
Copy link

minwe commented Jun 22, 2016

如何与全局样式共存

这一节的配置有误吧:想区分处理 stylesviews 两个文件夹里的样式,实际上写成一样了。

module: {
  loaders: [{
    test: /\.jsx?$/,
    loader: 'babel'
  }, {
    test: /\.scss$/,
    // `views` 下面的样式启用 CSS Modules
    exclude: path.resolve(__dirname, 'src/views'),
    loader: 'style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true'
  }, {
    test: /\.scss$/,
    include: path.resolve(__dirname, 'src/styles'),
    loader: 'style!css!sass?sourceMap=true'
  }]
}

@camsong
Copy link
Owner Author

camsong commented Jun 22, 2016

@minwe 没有错,两个是不一样的,第一个是 exclude,第二个是 include。只写一个是不行的,因为 webpack 的 loaders 是只要 match 的都会执行的,并不是 match 一个后面的就不执行了,所以每个 loader 的 includeexclude 都要写清楚

  {
    test: /\.scss$/,
    exclude: path.resolve(__dirname, 'src/styles'),
    loader: 'style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true'
  }, {
    test: /\.scss$/,
    include: path.resolve(__dirname, 'src/styles'),
    loader: 'style!css!sass?sourceMap=true'
  }]

@minwe
Copy link

minwe commented Jun 23, 2016

@camsong 惭愧,没留意到一个是 exclude,另一个是 include

@xiaoxiongmila
Copy link

如果我想结果Bootstrap导入类名的时候应该怎么导入呢,还请指教,貌似composes只能导入一个类名,否则的话就会报错

@AyumiKai
Copy link

学习了,谢谢楼主

@mapleshawxiao
Copy link

貌似类名的命名规则就不能class-name了而必须classname或者className?className={style.class-name}是有问题的

@KangdaOOLin
Copy link

但怎么跟html中的元素class名对应起来呢?比如,在css中定义了:

:local(.box) {
  ...
}

html文件中:

<div class="box"></div>

这样做的话,div.box没有发现对应的css属性。这个怎么解决?

@mapleshawxiao
Copy link

@wenkanglin 在html文件里面你需要import相应的scss,类似 import styles from './xxx.scss'
然后在html是这么调用的,styles.box

@NE-SmallTown
Copy link

NE-SmallTown commented Dec 11, 2016

@camsong 你好,请教下关于全局样式的问题。

引入全局样式的时候,比如上面提到的:

/* src/app.js */

import globalCss from './styles/app.scss';

这样会报错,因为eslint一般会设置变量定义了就必须使用,而这里globalCss是没有使用的,请问这个怎么解决好一点呢。

还有一点就是非驼峰命名的样式如果不用类似classnames这样的库该如何处理呢,或者说只能是按照他那样的逻辑,即调用一个工具函数传参然后返回一个字符串的方式吗

@cqupt-yifanwu
Copy link

您好,初入react 看到css modules 有个疑问,它是不是不支持嵌套的css选择器,如果我需要给一组标签(比如li)添加样式应该怎么做?如果有时间还麻烦您给解答一下,见笑了

@Evoque
Copy link

Evoque commented Jan 16, 2017

@camsong 上面说的:

如果我在 style 文件中使用了 id 选择器,伪类,标签选择器等呢? 没问题,所有这些选择器将不被转换,原封不动的出现在编译后的 css 中。也就是说 CSS Modules 只会转换 class 名相关样式。

我试了一下,id选择器是可以进行转换的 如我的 #btn{...} ==> Button_btn-3eNi-, 应用的时候也没有问题的。

@camsong
Copy link
Owner Author

camsong commented Jan 23, 2017

@Evoque 谢谢指正,已更正

没问题,所有这些选择器将不被转换,原封不动的出现在编译后的 css 中。也就是说 CSS Modules 只会转换 class 名和 id 选择器名相关的样式。

@qingmingsang
Copy link

@camsong
你好,请问 :export:import 的相关文档在哪里可以看到?

@camsong
Copy link
Owner Author

camsong commented Feb 13, 2017

@qingmingsang
Copy link

@camsong 谢谢

@hyhajnal
Copy link

你好,请问不同组件要@import一份sass的variables, 于是把它放在入口文件,但发现组件无法使用其变量

      {
        test: /\.scss$/,
        loader: 'style!css!sass'
      }

@camsong
Copy link
Owner Author

camsong commented Mar 18, 2017

@hyhajnal
Copy link

hyhajnal commented Mar 18, 2017

按照下边配置了一下,上边问题是解决了。而且神奇的是我在入口文件里没有import,也生效了。
不过我看了下最后打包后的页面重复引入了style,不知为何?@camsong

sassLoader: {
    data: '@import "base";',
    includePaths: [
      path.resolve(__dirname, "../static/css/help")
    ]
  }

@riskers
Copy link

riskers commented Mar 24, 2017

composes: $primary-color from './settings.css';

多了个 $

@wqcstrong
Copy link

我现在用的是react,但是关于样式方面遇到了一点问题。
想问一下,如果我不想用import styles from ...这种方式,例如下面这种;
import styles from '*.scss';
render(){ return( <div className={styles.container}> ) }
我想直接引入样式文件,像下面这么写:
//index.scss file
.container{...};
然后在index.js里面直接引入这个样式文件.
//index.js
import './index.scss'
render(){ return( <div className='container'> ... </div> ) }
能不能这样去写呢?我写了几个demo都行不通,但是我觉着应该可以的。可能是我哪里错了。还请各位大神指教啊! @camsong @mapleshawxiao

@qingmingsang
Copy link

@wqcstrong
试试 react-css-modules 或者不用 css modules

@wqcstrong
Copy link

@qingmingsang
react-css-modules可以实现我上面说的那种写法么?

@qingmingsang
Copy link

@wqcstrong 看看文档,应该能基本满足你的要求

@wqcstrong
Copy link

@qingmingsang
https://github.com/gajus/react-css-modulesreact-css-modules是这个链接么?翻一遍过来全是英文……如果是的话我就硬着头皮上了。。。
顺便麻烦问一下可以给个联系方式么?大神求给根腿毛抱抱。

@ChanJuanMei
Copy link

@camsong
请教博主大大:我现在遇到这样的问题

import styles from '../../../../styles/entrance/course.less';
 <div className="courseContainer">
 //这种写法courseContainer里的样式生效
 <div className={style.courseContainer}>
 //这样子courseContainer样式不会生效,检查元素的时候页面不会有courseContainer这个类名。

webpack配置文件:

module.exports = {
    options: {
          devtool: 'inline-source-map',
        entry: entries,
        output: {
            path: path.join(__dirname, '../../', '.tmp/public/'),
            publicPath: '/',
            filename: 'js/[name].js',
            chunkFilename: 'js/[name].[id].chunk.js'
        },
        module: {
            loaders: [
                {test: /\.css$/, loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader'})},
                {
                    test: /\.less/,
                    loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader!less-loader'})
                },
              {test : /containers[\/\\].+\.jsx/, use:[ 'bundle-loader?lazy', 'babel-loader?compact=false']},

                {test: /\.(js|jsx)$/, loader: 'babel-loader?compact=false'},
                {test: /\.(png|jpg|gif)$/, loader: 'url-loader?limit=3072'}
            ]
        },
        resolve: {
            extensions: ['.js', '.json', '.jsx']
        },
        plugins: [
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': '"development"',
                '__SERVER_RENDER__': '0',
                'SERVER_LIFT_TIME': JSON.stringify(Date.now())
            }),
            new webpack.ProvidePlugin({
                $: 'jquery',
                jQuery: 'jquery',
                moduleKeeper: path.join(__dirname, '../../', 'assets/js/dependencies/moduleKeeper.overws.js'),
                gLogger: path.join(__dirname, '../../', 'api/services/gLogger.js'),
                i18n: path.join(__dirname, '../../', 'api/services/i18n.js')
            }),
            new ExtractTextPlugin({filename: '/styles/[name].css', disable: false, allChunks: true}),
            new CopyWebpackPlugin([
                {from: path.join(__dirname, '../../', 'assets/images'), to: 'images'},
                {from: path.join(__dirname, '../../', 'assets/favicon.ico')},
            ])
        ]
    },
    watchOptions: {
        //ignored: path.join(__dirname, '../../', "/assets/js/dependencies/"),
        aggregateTimeout: 300,
        poll: 1000
    }
};

跪求解答,无限感谢

@ChanJuanMei
Copy link

@camsong 我想使用

这种方式,现在页面这样写无法生效

@qingmingsang
Copy link

@ChanJuanMei 你是不是 styles,写成了 style .
然后是貌似没开启 css modules

@ChanJuanMei
Copy link

@ForeverArt 没有解决,感觉还是有办法解决的,只是最近没时间弄研究

@lili21
Copy link

lili21 commented Jan 18, 2018

@ChanJuanMei 你并没有开启 css modules。

@YL2014
Copy link

YL2014 commented Jan 20, 2018

请教个问题,初始化样式不需要模块化,我的base.css在入口js里引入了(import 'base.css'),然后组件里引入的模块化的样式文件(import styles from 'app.scss'),webpack处理后,发现组件里的样式编译到初始化样式的前面去了,组件里就不方便覆盖全局的样式了,这种情况可能是哪里的问题啊

webpack配置里,css没有开启模块化,scss开启了

@2225025616
Copy link

2225025616 commented May 16, 2018

我的也是没有实现,css样式无用,并且打印的styles为空
import React, { Component } from 'react';
import Footer from '../footer';
import CSSModules from 'react-css-modules';
import styles from './index.less';

class Home extends Component {

render() {
console.log(styles)
return (


tips




);
}
}

export default CSSModules(Home, styles);

下面是我的webpack配置中module部分
`module.exports = {
.......
module: {
strictExportPresence: true,
rules: [

  {
    test: /\.(js|jsx|mjs)$/,
    enforce: 'pre',
    use: [
      {
        options: {
          formatter: eslintFormatter,
          eslintPath: require.resolve('eslint'),
          baseConfig: {
            extends: [require.resolve('eslint-config-react-app')],
          },
          ignore: false,
          useEslintrc: false,
        },
        loader: require.resolve('eslint-loader'),
      },
    ],
    include: paths.appSrc,
  },
  {
    oneOf: [
      {
        test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
        loader: require.resolve('url-loader'),
        options: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
      {
        test: /\.(js|jsx|mjs)$/,
        include: paths.appSrc,
        loader: require.resolve('babel-loader'),
        options: {
          // @remove-on-eject-begin
          babelrc: false,
          presets: [require.resolve('babel-preset-react-app')],
          // @remove-on-eject-end
          // This is a feature of `babel-loader` for webpack (not Babel itself).
          // It enables caching results in ./node_modules/.cache/babel-loader/
          // directory for faster rebuilds.
          cacheDirectory: true,
        },
      },
      // "css" loader resolves paths in CSS and adds assets as dependencies.
      // "style" loader turns CSS into JS modules that inject <style> tags.
      // In production, we use a plugin to extract that CSS to a file, but
      // in development "style" loader enables hot editing of CSS.
      {
        test: /\.(css|less)$/,
        use: [
          require.resolve('style-loader'),
          {
            loader: require.resolve('css-loader'),
            options: {
              importLoaders: 1,
              sourceMap: true,
              localIndexName:"[name]__[local]___[hash:base64:5]"
            },
          },
          {
            loader: require.resolve('less-loader'),
            options: {
              importLoaders: 1,
              sourceMap: true,
              localIndexName:"[name]__[local]___[hash:base64:5]"
            },
          },
        ],
      },
   
      {
        exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
        loader: require.resolve('file-loader'),
        options: {
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
    ],
  },
],

},

};`
less 部分
.home {
padding: 0.35rem 0.2rem;
.tips{
font-size: 0.28rem;
font-weight: "normal";
line-height: 25;
letter-spacing: 0;
text-align: "justify";
color: "#697b87";
}
}

@ChanJuanMei
Copy link

@lili21 我没有开启 css modules吗? 那我应该怎么做呢

@lili21
Copy link

lili21 commented May 31, 2018

@ChanJuanMei 你开启就可以了。

https://github.com/webpack-contrib/css-loader#options

css-loader默认并不开启css modules,需要自己传参数。

@zhanglina1990
Copy link

棒,学习了,我也要在项目中试试

@iamafresh
Copy link

snice

:octocat: From gitme Android

@shengxiao3587
Copy link

如果我想结果Bootstrap导入类名的时候应该怎么导入呢,还请指教,貌似composes只能导入一个类名,否则的话就会报错

@wenkanglin 在html文件里面你需要import相应的scss,类似 import styles from './xxx.scss'
然后在html是这么调用的,styles.box

貌似类名的命名规则就不能class-name了而必须classname或者className?className={style.class-name}是有问题的

因为中间的‘-’会被解析成减号,所以不能用class-name

@anyexinglu
Copy link

anyexinglu commented Jan 8, 2019

@camsong 请教个问题:

部门自用组件库样式方案,由scss改成scss+cssModules有哪些坑嘛?主要影响是:业务二十几个项目不能写组件覆盖样式了(也是部门老大的意思,希望把样式覆盖,改成由组件支持传入props.className,可控些。但个人以为,页面很多边角样式不遵循通用交互视觉样式,我们没法都支持传入,可能业务项目因不能覆盖样式而放弃我们的组件库)。

另外未来,几个月后希望公司内几个部门开源,半年内可能业界开源。

感谢关注!

@Lin-Kyle
Copy link

Lin-Kyle commented Jan 9, 2019

@camsong 请教一下,有没试过在umi里使用react-css-modules如果我用在纯函数里正常使用,但是如果是用在Class会报错

TypeError: Class constructor Loader cannot be invoked without 'new'

代码大概如下

import styles from './index.scss';
import CSSModules from 'react-css-modules';

class Page2 extends PureComponent {
  render() {
    return (
      <div styleName='page1_title'></div>
    );
  }
}

export default CSSModules(Page2, styles)

@camsong
Copy link
Owner Author

camsong commented Jan 9, 2019

@camsong 请教个问题:

部门自用组件库样式方案,由scss改成scss+cssModules有哪些坑嘛?主要影响是:业务二十几个项目不能写组件覆盖样式了(也是部门老大的意思,希望把样式覆盖,改成由组件支持传入props.className,可控些。但个人以为,页面很多边角样式不遵循通用交互视觉样式,我们没法都支持传入,可能业务项目因不能覆盖样式而放弃我们的组件库)。

另外未来,几个月后希望公司内几个部门开源,半年内可能业界开源。

感谢关注!

业务项目对组件样式定制场景很多,不支持覆盖很难。最常见的样式可以通过传入props.className来控制,对于一些边角样式绕不开个性化覆盖,这时候就要使用:gobal。建议把组件DOM树梳理清楚,设置合理的 class 名,全局覆盖的时候能精确定位。

@whusnoopy
Copy link

貌似类名的命名规则就不能class-name了而必须classname或者className?className={style.class-name}是有问题的

应该可以用 className={style['class-name']} 来引用

@camsong
Copy link
Owner Author

camsong commented Jan 18, 2019

@camsong 请教一下,有没试过在umi里使用react-css-modules如果我用在纯函数里正常使用,但是如果是用在Class会报错

TypeError: Class constructor Loader cannot be invoked without 'new'

代码大概如下

import styles from './index.scss';
import CSSModules from 'react-css-modules';

class Page2 extends PureComponent {
  render() {
    return (
      <div styleName='page1_title'></div>
    );
  }
}

export default CSSModules(Page2, styles)

没这样用过,目前 react-css-modules 已经废弃,你可以试一下 babel-plugin-react-css-modules

@wilsonyiyi
Copy link

@camsong 请教一下,我想实现对antd库某个组件的样式的覆盖修改,使用css-module尝试了无法实现,因为每个类都会被编译。虽然这个问题可以通过直接引用样式文件解决,但会全局污染。有什么办法可以通过css-module实现吗?

.custom-style-wrap {
	.ant-upload-list-picture-card {
		.ant-upload-list-item {
			width: 200px;
			height: 200px;
		}
	}
}

@camsong
Copy link
Owner Author

camsong commented Jul 3, 2019 via email

@zhangzhi93
Copy link

请问下怎样写组合器选择?比如,要重写某个自定义类下的直接组件类的样式:
.right{ :global{ >.ant-row{} } }
这样写会报错~ 请问下这种情况怎么写?同样还有其他的组合器+,~如何使用

@sogud
Copy link

sogud commented Feb 7, 2021

@camsong 请教一下,我想实现对antd库某个组件的样式的覆盖修改,使用css-module尝试了无法实现,因为每个类都会被编译。虽然这个问题可以通过直接引用样式文件解决,但会全局污染。有什么办法可以通过css-module实现吗?

.custom-style-wrap {
	.ant-upload-list-picture-card {
		.ant-upload-list-item {
			width: 200px;
			height: 200px;
		}
	}
}

可以这样写

.custom-style-wrap {
:global {
	.ant-upload-list-picture-card {
		.ant-upload-list-item {
			width: 200px;
			height: 200px;
		}
	}
}
}

@wmasfoe
Copy link

wmasfoe commented Jun 24, 2023

貌似类名的命名规则就不能class-name了而必须classname或者className?className={style.class-name}是有问题的

可以 styles['class-name']

@meloseven
Copy link

meloseven commented Jun 24, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests