Featured image of post React 入门与 JSX 语法详解

React 入门与 JSX 语法详解

从 React 设计哲学(关注点分离 vs 组件化)讲起,到 JSX 的所有语法细节(表达式嵌入、属性、Fragment、安全防护、列表 key),再到函数组件、children prop、条件渲染、列表渲染——一份 React 16+ 时代的入门手册。

为什么写这篇:2016 年 React 已经发布 4 年(v15.3 同期),Redux 单向数据流成为社区共识。理解 JSX 是理解 React 一切后续特性的基础——Hooks、Suspense、Server Components 全都建立在"JSX 描述 UI、React 把它变成真实 DOM"上。

适用读者:完全没接触过 React 的同学;从 Vue 迁过来的前端;想系统整理 React 基础的初中级工程师。

前置知识:HTML / CSS / JavaScript ES6+。

目录

  1. React 起源:组件化的胜利
  2. JSX 起源:UI 与逻辑的"反向"组合
  3. JSX 5 大语法特性
  4. 函数组件与 props
  5. 特殊 prop:children
  6. 条件渲染:&& 与三元运算符
  7. 列表渲染与 key 的正确用法
  8. JSX 工具链:VSCode 插件与转换器
  9. 实用资源

1. React 起源:组件化的胜利

在 React 出现之前,前端开发的"关注点分离"是按文件类型

1
2
3
- index.html   (结构)
- style.css    (样式)
- app.js       (行为)

这种模式在简单页面下很清晰。但随着应用变复杂:

  • 同一个按钮的样式散落在多个 CSS 文件
  • 一个交互逻辑跨多个 JS 文件
  • 改一个页面要切 3 个文件

React 团队认为:渲染逻辑和 UI 的其他逻辑(事件处理、状态变化、数据展示)是紧密耦合的。强行把它们按文件分开,反而让代码散落、难以维护

于是 React 提出"关注点分离的新方式"——按组件分:

1
2
3
4
5
6
7
8
9
src/
├── components/
│   ├── Button/
│   │   ├── Button.jsx    # 这按钮的模板、样式、行为全在一起
│   │   ├── Button.module.css
│   │   └── Button.test.js
│   └── UserCard/
└── pages/
    └── Home/

每个组件是一个松耦合、可复用的单元——内部结构、样式、行为都聚拢在一起,组件之间通过 props 通信。


2. JSX 起源:UI 与逻辑的"反向"组合

JSX 是 React 团队把"组件化"哲学落到代码上的工具。JSX = JavaScript + XML 语法

1
const element = <h1>Hello, world!</h1>

这行代码既不是字符串也不是 HTML——是JavaScript 表达式,最终会被 Babel/SWC 编译成 React.createElement('h1', null, 'Hello, world!')

Why JSX:让你用写 HTML 的方式写 UI,但保留 JavaScript 全部能力(变量、函数、逻辑判断)。


3. JSX 5 大语法特性

3.1 在 JSX 中嵌入 JavaScript 表达式

{} 嵌入任何 JavaScript 表达式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const name = 'React 爱好者'
const element = <h1>Hello, {name}!</h1>

function formatName(user) {
  return user.firstName + ' ' + user.lastName
}

const user = { firstName: 'Harper', lastName: 'Perez' }
const greetingElement = (
  <h1>Hello, {formatName(user)}!</h1>
)

const sum = <div>{2 + 2}</div>   // 4

只能放表达式,不能放语句

1
2
{ var a = 1 }       // ❌
{ if (ok) { ... } }  // ❌

3.2 JSX 本身也是表达式

JSX 可以赋值给变量、作为函数参数、作为返回值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function getGreeting(user) {
  if (user) {
    return <h1>Hello, {user.name}!</h1>
  }
  return <h1>Hello, Stranger.</h1>
}

const welcomeMessage = getGreeting({ name: 'Alice' })

// 数组 + map 渲染列表
const items = ['Apple', 'Banana', 'Orange']
const listItems = (
  <ul>
    {items.map((item, index) => (
      <li key={index}>{item}</li>
    ))}
  </ul>
)

3.3 JSX 中的属性

JSX 属性用驼峰命名(camelCase),不是 HTML 那种小写连字符:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// HTML: class / tabindex / onclick
// JSX : className / tabIndex / onClick

const element = (
  <a href="https://react.dev" target="_blank" className="my-link">
    React 官网
  </a>
)

const image = (
  <img
    src="avatar.jpg"
    alt="User Avatar"
    style={{ width: '100px', height: '100px' }}
  />
)

Why camelCase:JSX 最终编译成 JavaScript 对象,而 JavaScript 变量名不能用连字符。

HTML 属性JSX 属性
classclassName
tabindextabIndex
forhtmlFor
readonlyreadOnly
onclickonClick
onchangeonChange

3.4 JSX 子元素与 Fragment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 多个元素必须包一个父元素
const container = (
  <div>
    <h1>标题</h1>
    <p>这是一段文字</p>
    <img src="example.jpg" alt="示例图片" />
  </div>
)

// 用 Fragment 不增加 DOM 节点
const frag = (
  <>
    <p>第一段</p>
    <p>第二段</p>
  </>
)

Why Fragment:返回多个根节点时不污染 DOM 结构。<>...</><Fragment>...</Fragment> 的语法糖。

3.5 JSX 防注入攻击

1
2
3
const userInput = '<script>alert("hacked")</script>'
const safeElement = <div>{userInput}</div>
// 渲染为:<div>&lt;script&gt;alert("hacked")&lt;/script&gt;</div>

Why 安全:React 在渲染前会自动转义所有嵌入值——<&lt;>&gt;,所有用户输入都安全。


4. 函数组件与 props

4.1 传统函数组件

1
2
3
function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>
}

4.2 ES6 箭头函数

1
2
3
const Greeting = (props) => {
  return <p>Greetings, {props.name}!</p>
}

4.3 解构 props(推荐)

1
2
3
4
5
6
7
8
const UserInfo = ({ name, age }) => {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
    </div>
  )
}

4.4 接口定义 props(TypeScript,最推荐)

1
2
3
4
5
6
7
interface UserGreetProps {
  readonly isLog: boolean
}

function UserGreet({ isLog }: UserGreetProps) {
  return <h1>{isLog ? 'Logged in' : 'Logged out'}</h1>
}

4.5 必知:React 组件函数只能接一个 props 对象

1
2
3
4
5
// ❌ 错误——React 会传整个 props 对象,不是单个布尔值
function UserGreet(isLog: boolean) { }

// ✅ 正确
function UserGreet(props: { isLog: boolean }) { }

5. 特殊 prop:children

children 是组件内部的"插槽"——父组件传入的所有 JSX 都通过 children 拿到。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// components/Layout.tsx
import React from 'react'

interface LayoutProps {
  readonly title: string
  readonly children?: React.ReactNode
}

function Layout({ title, children }: LayoutProps) {
  return (
    <div style={{ border: '10px solid #eee', padding: '20px' }}>
      <h1>{title}</h1>
      <div>{children}</div>
    </div>
  )
}

export default Layout
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// App.tsx
import Layout from './components/Layout'

function App() {
  return (
    <Layout title="用户管理系统">
      <p>这是应用的主要内容。</p>
      <button>点击我</button>
    </Layout>
  )
}

实际效果title 是 prop,children 是 prop 的特殊形式——React 把 <p>...</p><button>...</button> 整个打包成 children 数组传给 Layout。


6. 条件渲染:&& 与三元运算符

6.1 && 短路运算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function Mailbox(props) {
  const unreadMessages = props.unreadMessages
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>您有 {unreadMessages.length} 条未读消息</h2>
      }
    </div>
  )
}

原理A && BAtrue 时返回 B;当 Afalse 时返回 A(React 忽略 false / null / undefined)。

6.2 三元运算符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function Greeting(props) {
  return (
    <div>
      {props.isLoggedIn
        ? <h1>欢迎回来</h1>
        : <h1>请先登录</h1>
      }
    </div>
  )
}

vs if/else:三元表达式可以在 JSX 中内联if/else 必须提到 JSX 之外。

6.3 阻止组件渲染

1
2
3
4
5
6
function WarningBanner(props) {
  if (!props.warn) {
    return null   // 渲染空——不显示任何东西
  }
  return <div className="warning">警告</div>
}

return null 不会卸载组件——生命周期(包括 Hooks)仍会调用。


7. 列表渲染与 key 的正确用法

7.1 基础列表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function NumberList(props) {
  const numbers = props.numbers
  const listItems = numbers.map((number) => (
    <li key={number.toString()}>{number}</li>
  ))
  return <ul>{listItems}</ul>
}

const numbers = [1, 2, 3, 4, 5]
<NumberList numbers={numbers} />

7.2 key 的作用

key 是 React 用于识别列表中每个元素的特殊字符串属性。当列表项顺序变化或增删时,key 帮助 React 高效更新 DOM——避免不必要的重新创建。

7.3 key 的选择

1
2
3
4
5
6
// ✅ 用数据里的稳定唯一 ID
const todos = [{ id: 't1', text: '吃饭' }, { id: 't2', text: '睡觉' }]
todos.map(t => <li key={t.id}>{t.text}</li>)

// ❌ 避免用 index(除非列表完全静态,永不变化)
todos.map((t, i) => <li key={i}>{t.text}</li>)

Why 避免 index 当 key:当你在列表头部插入一条新数据,所有 index 都会 +1,React 误以为"所有项都变了"——性能暴跌,还可能引发输入框焦点错位、动画异常等 bug。


8. JSX 工具链:VSCode 插件与转换器

8.1 VSCode 必装插件

插件用途
ES7+ React/Redux/React-Native snippetsrfc / rcc / useState 等代码片段
ESLint代码规范
Prettier自动格式化
Auto Rename Tag改开始标签自动改结束标签
Codeium(或 GitHub Copilot)AI 补全

常用代码片段前缀:

前缀展开为
rfcfunction 组件
rccclass 组件
useStateconst [val, setVal] = useState(0)
useEffectuseEffect(() => {}, [])
imrimport React from 'react'

8.2 浏览器插件

React Developer Tools(Chrome / Firefox 扩展)—— 装上后 DevTools 多两个 Tab:Components(看组件树和 props)和 Profiler(看组件渲染性能)。

8.3 HTML ↔ JSX 转换器

transform.tools/html-to-jsx——把 HTML 片段粘进去,自动转 JSX(自动加 className、闭合自闭合标签等)。

8.4 入门教程

VSCode 官方 React 教程——30 分钟跑通第一个 React 应用。


9. 实用资源

9.1 文档

9.2 学习路径

  1. JSX + 函数组件(本文)
  2. 状态管理:useState / useReducer
  3. 副作用:useEffect / useLayoutEffect
  4. 组件通信:Props / Context / 状态管理库
  5. 性能优化:memo / useMemo / useCallback
  6. 高级特性:Suspense / ErrorBoundary / Server Components

小结

React 入门核心:

  1. 设计哲学:UI 与逻辑聚拢在"组件"里,组件之间通过 props 通信
  2. JSX:HTML-like 语法 + JS 能力,编译成 React.createElement 调用
  3. JSX 5 大特性:表达式嵌入、属性 camelCase、Fragment、children、auto-escape
  4. 函数组件 + props:现代 React 的"最小单位"
  5. 条件渲染:&& 短路、三元表达式、return null
  6. 列表渲染 + key:永远用稳定 ID 当 key,绝不用 index

下一步:学 Hooks(useState / useEffect / useContext / useReducer)——这是 React 16.8+ 引入的"函数式组件能力扩展",是现代 React 一切的基础。读懂 Hooks 后,再学状态管理(Redux / Zustand)、路由(React Router)、数据请求(React Query / SWR)就水到渠成。

参考资料

使用 Hugo 构建
主题 StackJimmy 设计