2、JSX语法深入:表达式、条件渲染、列表渲染与样式处理
好,咱们接着聊JSX。上一章我带你写了个简单的Hello World,你可能觉得「就这?不就是HTML套了个壳吗?」
嗯,我当时也是这么想的。直到我在项目中踩了几个坑,才发现JSX远没那么简单。这一章,我把JSX里最常用的几个硬核知识点掰开揉碎讲给你听。
2.1 JSX表达式:花括号里能写什么?
JSX里,只要看到{},就代表你要写JavaScript表达式了。说白了,花括号就是JSX和JS之间的桥梁。
核心规则:花括号里只能放表达式,不能放语句。
什么是表达式?能算出值的东西就是表达式。比如:
const name = '张三';
const age = 25;
// ✅ 正确:这些都是表达式
const element = (
<div>
<p>姓名:{name}</p>
<p>年龄:{age + 1}</p>
<p>是否成年:{age >= 18 ? '是' : '否'}</p>
<p>当前时间:{new Date().toLocaleString()}</p>
</div>
);
// ❌ 错误:这是语句,不能放在花括号里
// const wrong = <div>{if (age > 18) { return '成年' }}</div>;
我个人习惯把复杂的表达式抽出来,先算好再放进去。比如:
const isAdult = age >= 18;
const statusText = isAdult ? '已成年' : '未成年';
const element = <p>状态:{statusText}</p>;
这样代码更干净,调试也方便。我在项目中见过有人把一长串三元运算符嵌套在花括号里,那阅读体验……嗯,你懂的。
2.2 条件渲染:显示什么,我说了算
实际开发中,经常要根据数据状态决定显示什么。React里做条件渲染,我常用的有四种方式。
2.2.1 三元运算符(最常用)
function Greeting({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? (
<h2>欢迎回来!</h2>
) : (
<button>请登录</button>
)}
</div>
);
}
这个写法最直观,我90%的条件渲染都用它。
2.2.2 && 运算符(简洁但要注意)
function Notification({ message }) {
return (
<div>
{message && <p>提示:{message}</p>}
</div>
);
}
这里有个坑,我曾经踩过:如果message是数字0,0 && <p>...</p>会直接渲染出0。因为0是假值,但React会把它渲染到页面上。所以用&&时,确保左边一定是布尔值。
避坑指南:我曾经在列表渲染时用 list.length && <List />,结果列表为空时页面上出现了一个大大的 0。正确的写法是 list.length > 0 && <List />。
2.2.3 if/else 抽成函数
function getStatusText(status) {
if (status === 'loading') return '加载中...';
if (status === 'error') return '出错了';
if (status === 'success') return '加载成功';
return '未知状态';
}
function StatusPage({ status }) {
return <p>{getStatusText(status)}</p>;
}
条件分支超过3个时,我建议用这种方式。JSX里保持干净,逻辑抽出去。
2.2.4 对象映射(优雅但小众)
const STATUS_MAP = {
loading: '加载中...',
error: '出错了',
success: '加载成功',
};
function StatusPage({ status }) {
return <p>{STATUS_MAP[status] || '未知状态'}</p>;
}
这个写法我个人很喜欢,尤其适合枚举类型的条件渲染。代码量少,扩展也方便。
2.3 列表渲染:把数据变成DOM
渲染列表,核心就一句话:用map()方法遍历数组,返回JSX元素。但有个关键点——key。
const users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' },
];
function UserList() {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
关于key,记住三条:
- key要稳定唯一,优先用数据的id
- 不要用数组索引作为key(除非列表永远不会增删改排序)
- key只在兄弟节点间比较,不同列表可以用相同key值
为什么不能用索引?我曾经在一个可拖拽排序的列表里用了索引作为key,结果拖拽后整个列表的DOM都乱了,复选框的状态全串了。那次排查花了我两个小时……后来老老实实改用id,问题瞬间解决。
2.4 JSX样式处理:内联样式与CSS类名
2.4.1 内联样式
JSX里的内联样式,写法跟HTML不一样。它接收一个对象,属性名用驼峰命名。
const styleObj = {
color: 'blue',
fontSize: '20px', // 注意:font-size 变成 fontSize
backgroundColor: '#f0f0f0', // background-color 变成 backgroundColor
padding: '10px 20px',
};
function StyledComponent() {
return <div style={styleObj}>这是一段带样式的文字</div>;
}
// 也可以直接写在JSX里
function InlineStyle() {
return (
<div style={{ color: 'red', fontWeight: 'bold' }}>
红色加粗文字
</div>
);
}
我个人不太推荐大量使用内联样式。一来性能不如class,二来没法用伪类(hover、focus等)。但动态样式(比如根据状态改变颜色)用内联很方便。
2.4.2 CSS类名
JSX里用className代替HTML的class。为什么?因为class是JavaScript的保留关键字。
// 单个类名
<div className="container">内容</div>
// 多个类名
<div className="container active highlighted">内容</div>
// 动态类名(常用)
function Button({ isPrimary }) {
return (
<button className={`btn ${isPrimary ? 'btn-primary' : 'btn-secondary'}`}>
点击
</button>
);
}
小技巧:当类名逻辑复杂时,可以用第三方库 classnames 或自己写个工具函数。我项目中常用的是:cn('base-class', { 'active': isActive, 'disabled': isDisabled })。
2.5 JSX注意事项:这些坑你别踩
最后,我总结几个JSX里容易出问题的地方。
| 注意事项 | 说明 | 示例 |
|---|---|---|
| 只能有一个根元素 | JSX表达式必须被一个闭合标签包裹 | 用 <React.Fragment> 或 <></> 包裹 |
| 标签必须闭合 | 自闭合标签要写 /> |
<img src="..." /> 不能写成 <img src="..."> |
| 属性名用驼峰 | HTML属性转JSX属性要改写法 | onclick → onClick,tabindex → tabIndex |
| 注释要写在花括号里 | JSX里不能用HTML注释 | {/* 这是注释 */} |
| 布尔属性要小心 | disabled 等属性传 true 或 false |
<button disabled={isDisabled}>点击</button> |
我曾经踩过的坑:在JSX里写 <input type="text" /> 时忘了加自闭合的斜杠,结果整个页面渲染不出来。还有一次把 className 写成了 class,控制台报错找了半天。这些细节,写多了自然就记住了。
好了,JSX的核心语法就这些。你想想看,其实并不复杂,就是一些规则需要记牢。下一章我们开始讲组件,那才是React真正精彩的地方。