为组件添加样式
React 本身没有样式系统。你写的是普通 CSS,JSX 给了你两种方式把它附加到元素上:类名(能处理几乎所有情况)和 style 属性(用于样式表提前无法知道的那些值)。本章介绍这两种方法,以及夹在它们之间的模式:根据 props 或状态来生成类名,让元素随着你的数据改变而自动重新设置样式。
JSX 中的类名
class 是 JavaScript 的保留字,所以 JSX 用 className 这个属性名。它的值是一个普通字符串,React 会把它设置为 DOM 中元素的 class 属性,这意味着另一边的 CSS 写法和之前完全一样。
function Avatar() {
return <img className="avatar avatar--large" src={photo} alt="王女士" />
}一个字符串里两个类名,用空格分隔,和 HTML 一样。JSX 中的驼峰命名规则适用于标记里的属性名;但类名是你自己的,所以 kebab-case、BEM 或你样式表已经在用的任何命名约定都原样保留。
导入样式表
一个组件文件在顶部用一个简单导入语句来引入它需要的 CSS:
import './App.css'
export default function App() {
return <h1 className="title">React Facts</h1>
}这个导入左边没有变量,组件也从不读取这个文件。它是对构建工具的指令:这个模块依赖那个 CSS,所以把它包括进来。Vite 在开发时会把规则注入页面,在生产构建时生成一个真正的 .css 文件。搭建 React 项目 描述的是处理图片的同一种机制。
导入放在哪里是组织问题。在 Button.jsx 的顶部导入 Button.css 可以把样式和使用它的组件放在一起。不过规则本身是全局的:一个 .title 选择器会匹配页面任何地方带有那个类的元素,不管是哪个文件导入的。BEM 这样的命名约定存在的目的就是管理这个共享的命名空间,CSS Modules 和 CSS-in-JS 库可以在应用超出命名纪律时给你限定规则的作用域。
版本说明
React 19 增加了从组件内部渲染 <link rel="stylesheet"> 和 <style> 的支持,用一个 precedence 属性让 React 把它们提升到文档头部并按可预测的顺序排列。更早的版本需要用像 react-helmet 这样的库。上面的构建工具导入在每个版本中表现相同。
依赖数据的类名
className 像其他任何 JSX 表达式一样写在花括号里,所以字符串可以被计算。比如一个掷骰子游戏,每个骰子可以在掷骰之间被"锁定"。一个骰子是否被锁定这个状态存在状态中更上层的地方,作为属性传过来:
function Die({ value, isHeld }) {
return (
<button className={`die ${isHeld ? 'die--held' : ''}`}>
{value}
</button>
)
}每个骰子都有 die 类。被锁定的还会加上 die--held 类。样式表里放着真正的颜色和边框,组件的唯一工作就是决定哪个类应该被应用。当 isHeld 改变时,React 更新 class 属性,浏览器重新绘制。组件内部没有任何东西知道"锁定"长什么样,所以这个外观可以在不改动这个文件的情况下改变。
有一个小问题:当 isHeld 是 false 时,模板字符串会产生带尾部空格的 "die "。浏览器会忽视它,但在开发者工具里看起来不整洁。一个数组处理得更干净,也能应对多个条件:
const classes = ['die', isHeld && 'die--held'].filter(Boolean).join(' ')
return <button className={classes}>{value}</button>isHeld && 'die--held' 在条件不满足时计算为 false,filter(Boolean) 把它过滤掉,join(' ') 在留下来的东西之间放一个空格。
style 属性
style 属性接受一个 JavaScript 对象。属性名是 CSS 属性名的驼峰写法,双花括号是通常的表达式位置加上一个对象字面量:
<div style={{ backgroundColor: 'darkslateblue', paddingTop: 12 }}>
内联样式
</div>background-color 变成 backgroundColor,padding-top 变成 paddingTop。值通常是字符串。传一个数字,React 会为长度属性加上 px,所以 paddingTop: 12 渲染成 padding-top: 12px。CSS 视为无单位的属性保持原始数字:lineHeight、opacity、flexGrow、zIndex 和 fontWeight 都直接传给浏览器。任何其他单位都必须写成字符串,比如 width: '60%' 或 margin: '2rem'。
大多数样式应该在样式表里,一个类可以包含十几个声明,被十几个元素共用。style 属性的用处在于当一个值只在运行时才知道,而 CSS 无法提前知道它:
function ProgressBar({ percent }) {
return (
<div className="progress">
<div className="progress__fill" style={{ width: `${percent}%` }} />
</div>
)
}填充条的颜色、高度、过渡和圆角都在 .progress__fill 里。样式表唯一无法知道的值是宽度,因为它来自数据,所以这一个值写成内联。一个背景图像的 URL 来自 API 也是同样的模式:
<div className="hero" style={{ backgroundImage: `url(${photo.url})` }} />大小、定位和遮罩都在 .hero 里,只有 URL 通过 prop 传过来。
内联样式也有硬限制。一个 style 对象描述一个元素的声明,所以无法用它表达悬停状态、媒体查询或关键帧动画。那些无论其他样式多么动态都应该在 CSS 里。
className 附加(因为 class 是 JavaScript 的保留字)。 在文件顶部导入你的样式表,把规则写在里面,让组件通过从 props 或状态生成字符串来决定哪个类应用。
把 style 属性留给你的 CSS 文件提前无法知道的值,比如来自数据的宽度。
下一个:Props,组件开始从它们的父亲那里获取数据。

