如何使React中的CSS导入具有组件范围?

116

我有几个组件,它们具有以下CSS/组件结构

About/style.css

.AboutContainer {
    # Some style
}

p > code {
    # Some style
}
在组件中,我如下导入CSS:

About/index.js

import './style.css';

export default class About extends Component {
    render() {
         # Return some component
    }
}

然而,CSS是在<header>部分导入并保持全局范围。

我原本期望CSS应该是:

  1. 以组件为作用域,只应用于仅在此组件内呈现的元素。
  2. 如果卸载了该组件,该组件的样式将消失。

然而,在浏览器中进行检查时,这些样式是在<header>部分指定的,并且应用于所有组件。

<header>
   // Stuff
   <style type="text/css">style for component About</style>
   <style type="text/css">style for component B</style>
   <style type="text/css">style for component C</style>
   // Stuff
</header>

如何将CSS导入到组件范围内?似乎我对React ES6中的CSS导入有误解。

我正在遵循这个教程


编辑

Brett的答案是正确的。不过,我的问题实际上出在别处。我使用create-react-app创建了我的应用程序,它基本上简化了使用React所需的设置。它包括Webpack、Babel和其他必要的东西以便开始。它使用的默认Webpack配置没有为css-loader设置模块选项,所以它默认为false,因此未启用局部作用域。

只是为了提供额外的信息,看起来create-react-app没有直接自定义Webpack配置的简单方法,但似乎有许多针对web的如何解决的方法。


1
看一下 CSS 模块 - https://github.com/css-modules/css-modules - Brett DeWoody
9
Stackoverflow有些问题,当一个提问得当的问题被回答为“这个闪亮的库解决了你的问题”,却没有解决实际问题。虽然问题被更新并得到正确答案(感谢@Harry Cho!),但未获得任何赞同票数。 - Roy Prins
根据这篇博客,您可以在不弹出的情况下使用css模块与create-react-app:https://www.robinwieruch.de/create-react-app-css-modules/ - Stephane L
2
将mystyle.css重命名为mystyle.module.css并导入它以在使用create-react-app创建的React应用中使用CSS模块。 - kodebot
6个回答

81

听起来像是CSS Modules,或者其他许多 CSS-in-JS 包可以做到你想要的。其他包括Emotion(我目前最喜欢的),Styled Components,或在这里的其他包。

CSS Module 是一个 CSS 文件,其中所有类名和动画名称默认情况下都是本地作用域。所有 URL(url(...)) 和 @imports 都以模块请求格式(./xxx 和 ../xxx 表示相对路径,xxx 和 xxx/yyy 表示在 modules 文件夹中)。

以下是一个快速的示例:

假设我们有一个 React 组件:

import React from 'react';
import styles from './styles/button.css';

class Button extends React.Component {
  render() {
    return (
      <button className={styles.button}>
        Click Me
      </button>
    );
  }
}
export default Button;

./styles/button.css 文件中添加一些 CSS:

.button {
  border-radius: 3px;
  background-color: green;
  color: white;
}

CSS Modules完成其操作后生成的CSS将类似于:

.button_3GjDE {
  border-radius: 3px;
  background-color: green;
  color: white;
}

其中_3DjDE是一个随机生成的哈希值——为CSS类提供了独一无二的名称。

替代方案

一个更简单的替代方案是避免使用通用选择器(如pcode等)并采用基于类名的命名约定来定义组件和元素。即使像BEM这样的约定也可以帮助避免你所遇到的冲突。

将其应用到你的示例中,你可能会选择:

.aboutContainer {
  # Some style
}

.aboutContainer__code {
  # Some style
}

基本上,您需要样式化的所有元素都会获得唯一的类名。


24
也许react-scoped-css可以帮到你。顺便说一下,我是这个库的作者,如果你发现有任何问题或者想要改进它,你可以随时提出 issues 或发送一个 pr。

很好,但Sass呢? - Nikhil Nayyar
1
实际上,您可以添加任何预处理器。 - Liang
这对我不起作用... - kspc1000
1
经过数小时的调试,我意识到这个包与 Framer Motion 组件不兼容,例如 motion.button、motion.input 等。 - kspc1000

17

11

你可以使用SASS(.scss)来模拟作用域CSS。

比如说你只需要在一个组件中使用bootstrap(以避免冲突),那么将该组件包装在<div className='use-bootstrap'>中,然后创建一个.scss文件,像这样:

.use-bootstrap {   
  // Paste bootstrap.min.css here
}

1
这并不能解决整个问题 - 如果使用sass嵌套,CSS选择器将被连接起来,但是标记呢?内部选择器仍然有可能不唯一。 - Eliran Malka
这个可以工作,但是用于 @font-face 的相关路径会导致控制台出现错误... - Akber Iqbal
这对我有效。我使用以下代码包含了整个Bulma框架:.use-bulma { @import "bulma"; } - DanielG

7
使用以下文件命名规则:[name].module.css,并参阅文档:https://create-react-app.dev/docs/adding-a-sass-stylesheet

JSX文件

import React from 'react';
import styles from './MyPage.module.scss';

const MyPage = () => {
    return (
        <div className={styles.myDiv}>
            My Page
        </div>
    );
};

export default MyPage;

样式文件

    .myDiv {
        color: #f3f3f3;
        font-family: "Cambria";
        font-weight: normal;
        font-size: 2rem;
    }

这个不起作用。 - user2793618

1

对我来说,简单的解决方案(不使用:Css-modules或css-in-js)是在您的类选择器中添加后缀,如下所示:

className="btn__suffix"

如果你的组件名为:FileUpload.tsx,那么你的__suffix应该是__fu,我取了每个单词的第一个字符(这里是FileUpload)。

最终结果将会是:

import './style.css';

export default class About extends Component {
render() {
      Return (
         <div className="container__fu">
           ...
         </div>
              )
   }
}

在 CSS 部分,你的文件将会是:

.container__fu {
   ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接