返回首页

说说 React 中引入 CSS 的方式有哪几种?区别?

问题解析(面试官考察点)

面试官通过此问题主要考察:

  • 对 React 样式解决方案的了解
  • 能否根据项目需求选择合适的方式
  • 对 CSS 作用域和模块化理解
  • 对现代 CSS-in-JS 方案的掌握

核心概念(基础知识点)

理想的 CSS 解决方案

一个好的 React CSS 解决方案应该具备:

  • 局部作用域:不会随意污染其他组件
  • 动态样式:可以根据组件状态生成样式
  • 完整 CSS 特性:支持伪类、动画、媒体查询
  • 开发体验:编写简洁、有代码提示

四种主要方式

  1. 在组件内直接使用:内联样式
  2. 组件中引入 .css 文件:传统 CSS
  3. 组件中引入 .module.css 文件:CSS Modules
  4. CSS in JS:styled-components、emotion 等

详细解答(代码示例)

方式一:在组件内直接使用(内联样式)

import React, { Component } from "react";

// 定义样式对象
const div1 = {
  width: "300px",
  margin: "30px auto",
  backgroundColor: "#44014C", // 驼峰命名
  minHeight: "200px",
  boxSizing: "border-box"
};

class Test extends Component {
  render() {
    return (
      <div>
        {/* 使用样式对象 */}
        <div style={div1}>123</div>

        {/* 直接定义样式 */}
        <div style={{ backgroundColor: "red", padding: "20px" }}>
          直接内联样式
        </div>
      </div>
    );
  }
}

export default Test;

优点:

  • 样式局部有效,不会冲突
  • 可以动态获取 state 状态

缺点:

  • 需要使用驼峰命名
  • 没有代码提示
  • 大量样式导致代码混乱
  • 不支持伪类/伪元素

方式二:组件中引入 .css 文件

/* App.css */
.title {
  color: red;
  font-size: 20px;
}

.desc {
  color: green;
  text-decoration: underline;
}
import React, { PureComponent } from 'react';
import './App.css'; // 引入 CSS 文件

export default class App extends PureComponent {
  render() {
    return (
      <div className="app">
        <h2 className="title">我是 App 的标题</h2>
        <p className="desc">我是 App 中的一段文字描述</p>
      </div>
    );
  }
}

优点:

  • 符合日常编写习惯
  • 支持所有 CSS 特性

缺点:

  • 全局作用域,样式会互相影响
  • 容易产生命名冲突

方式三:引入 .module.css 文件(CSS Modules)

/* App.module.css */
.title {
  color: red;
  font-size: 20px;
}

.desc {
  color: green;
  text-decoration: underline;
}
import React, { PureComponent } from 'react';
import styles from './App.module.css'; // 作为模块引入

export default class App extends PureComponent {
  render() {
    return (
      <div className={styles.app}>
        <h2 className={styles.title}>我是 App 的标题</h2>
        <p className={styles.desc}>我是 App 中的一段文字描述</p>
      </div>
    );
  }
}

webpack 配置:

{
  test: /\.css$/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        modules: true // 启用 CSS Modules
      }
    }
  ]
}

优点:

  • 局部作用域,不会污染其他组件
  • 编译后生成唯一类名

缺点:

  • 类名不能使用连接符(如 .xxx-xx)
  • 必须使用 {styles.className} 形式
  • 不方便动态修改样式

方式四:CSS in JS(styled-components)

// style.js
import styled from 'styled-components';

export const SelfLink = styled.div`
  height: 50px;
  border: 1px solid red;
  color: yellow;
`;

export const SelfButton = styled.div`
  width: 150px;
  height: 150px;
  color: ${props => props.color};
  background-image: url(${props => props.src});
  background-size: 150px 150px;

  /* 支持伪类 */
  &:hover {
    opacity: 0.8;
  }

  /* 支持媒体查询 */
  @media (max-width: 768px) {
    width: 100px;
    height: 100px;
  }
`;
import React, { Component } from "react";
import { SelfLink, SelfButton } from "./style";

class Test extends Component {
  render() {
    return (
      <div>
        <SelfLink title="People's Republic of China">
          app.js
        </SelfLink>
        <SelfButton
          color="palevioletred"
          style={{ color: "pink" }}
          src={fist}
        >
          SelfButton
        </SelfButton>
      </div>
    );
  }
}

export default Test;

优点:

  • 真正的局部作用域
  • 支持动态样式(基于 props)
  • 支持所有 CSS 特性
  • 样式与组件紧密结合

缺点:

  • 需要学习新的语法
  • 增加运行时开销
  • 包体积增大

深入理解(原理剖析)

CSS Modules 的工作原理

/* Button.module.css */
.button {
  background: blue;
}
.primary {
  color: white;
}

编译后:

.Button_button__3K-4z {
  background: blue;
}
.Button_primary__2K-9x {
  color: white;
}
// 导入的是一个对象
import styles from './Button.module.css';

console.log(styles);
// {
//   button: 'Button_button__3K-4z',
//   primary: 'Button_primary__2K-9x'
// }

styled-components 的工作原理

const Button = styled.button`
  background: ${props => props.primary ? 'blue' : 'white'};
  color: ${props => props.primary ? 'white' : 'black'};
`;

执行过程:

  1. 解析模板字符串,提取 CSS
  2. 生成唯一的 class 名
  3. 创建 style 标签插入到 head
  4. 返回带 class 的 React 组件

四种方式对比

特性 内联样式 普通 CSS CSS Modules CSS in JS
作用域 局部 全局 局部 局部
动态样式 支持 不支持 需要配合 原生支持
伪类/媒体查询 不支持 支持 支持 支持
代码量 中等 中等
学习成本 中等
运行时开销

最佳实践

推荐方案选择

  1. 小型项目 / 原型开发

    • 使用 CSS Modules
    • 简单、无额外依赖
  2. 中大型项目

    • 使用 styled-components 或 emotion
    • 更好的开发体验和可维护性
  3. 需要与设计师协作

    • 使用普通 CSS + BEM 命名规范
    • 设计师可以直接修改 CSS 文件

styled-components 最佳实践

// 主题配置
const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    success: '#28a745'
  },
  breakpoints: {
    mobile: '768px',
    tablet: '1024px'
  }
};

// 基础组件
const Button = styled.button`
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  background: ${props => props.theme.colors.primary};
  color: white;
  cursor: pointer;

  &:hover {
    opacity: 0.9;
  }

  @media (max-width: ${props => props.theme.breakpoints.mobile}) {
    padding: 8px 16px;
  }
`;

// 扩展样式
const PrimaryButton = styled(Button)`
  background: ${props => props.theme.colors.primary};
`;

const DangerButton = styled(Button)`
  background: red;
`;

// 使用
function App() {
  return (
    <ThemeProvider theme={theme}>
      <PrimaryButton>主要按钮</PrimaryButton>
      <DangerButton>危险按钮</DangerButton>
    </ThemeProvider>
  );
}

CSS Modules 最佳实践

/* Button.module.css */
/* 使用 camelCase 命名 */
.button {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
}

.buttonPrimary {
  composes: button; /* 复用样式 */
  background: blue;
  color: white;
}

.buttonLarge {
  composes: button;
  padding: 15px 30px;
  font-size: 18px;
}
import styles from './Button.module.css';

function Button({ primary, large, children }) {
  const className = [
    primary ? styles.buttonPrimary : styles.button,
    large && styles.buttonLarge
  ].filter(Boolean).join(' ');

  return <button className={className}>{children}</button>;
}

避免样式冲突的策略

// 1. 使用 CSS Modules
import styles from './Component.module.css';

// 2. 使用 BEM 命名规范
// Block__Element--Modifier
// .user-card__title--large

// 3. 使用 CSS in JS
const Component = styled.div``;

// 4. 使用 Scoped CSS(Vue 风格)
// 配合 postcss 插件实现

动态样式处理

// styled-components 动态样式
const Button = styled.button`
  background: ${props => props.variant === 'primary' ? 'blue' : 'gray'};
  opacity: ${props => props.disabled ? 0.5 : 1};
`;

// CSS Modules + classnames 库
import classNames from 'classnames';
import styles from './Button.module.css';

function Button({ variant, disabled, children }) {
  const className = classNames({
    [styles.button]: true,
    [styles.buttonPrimary]: variant === 'primary',
    [styles.buttonDisabled]: disabled
  });

  return <button className={className}>{children}</button>;
}

面试要点

  1. 四种引入方式

    • 内联样式:简单但功能有限
    • 普通 CSS:全局作用域,易冲突
    • CSS Modules:局部作用域,编译时处理
    • CSS in JS:运行时处理,功能最强大
  2. 选择依据

    • 项目规模
    • 团队技术栈
    • 性能要求
    • 维护性考虑
  3. CSS Modules 原理

    • 编译时转换类名
    • 生成唯一标识
    • 通过对象映射访问
  4. CSS in JS 优缺点

    • 优点:动态样式、局部作用域、JS 逻辑
    • 缺点:运行时开销、包体积、学习成本
  5. 常见面试题

    • React 中引入 CSS 的方式有哪些?
    • CSS Modules 的原理是什么?
    • styled-components 的优缺点?
    • 如何解决 CSS 全局污染问题?
  6. 现代趋势

    • CSS in JS 越来越流行
    • Tailwind CSS 原子类方案
    • 服务端组件可能影响 CSS in JS