🔥 React 零基础入门(上):环境搭建 + JSX 深度解析

0 阅读8分钟

🔥 React 零基础入门(上):环境搭建 + JSX 深度解析

摘要:本文面向零基础开发者,从 Vite 环境搭建开始,带你掌握 React 组件思想和 JSX 的 9 条核心规则。JSX 作为 React 的灵魂,会从编译原理讲到 Fiber 架构,帮你彻底搞懂这个"看起来像 HTML 的 JavaScript"。


📌 前言

面试官:"请解释一下 JSX 的本质?"

我:"JSX 就是……在 JavaScript 里写 HTML?"

面试官:"那它和模板引擎有什么区别?为什么 React 要选它?编译后是什么?"

我:"……"

写了三个月 React,我连最基础的概念都没搞清楚。后来我花了两周时间,从零开始系统学习,终于把这些知识彻底理顺了。

这是React 零基础入门系列的上篇,重点讲 JSX——React 的灵魂。不讲废话,只讲你真正需要知道的东西。

📖 系列文章:本篇是上篇(环境搭建 + 组件 + JSX),下篇讲 Props、State、useEffect 生命周期、事件处理和实战项目。


🎯 本文适合谁

  • 完全没接触过 React 的前端新手
  • 写了 JSX 但不理解其本质的开发者
  • 从 Vue 转 React,对"模板 → JSX"感到困惑的同学
  • 想要在面试中讲清楚 JSX 的求职者

🚀 一、环境搭建(5 分钟搞定)

1.1 为什么选 Vite?

React 官方已不再推荐 create-react-app(CRA),现在推荐使用 Vite

对比项CRA(旧方案)Vite(新方案)
启动速度慢(Webpack 打包)快(ESBuild 原生编译)
热更新一般极快(毫秒级)
官方推荐❌ 已废弃✅ 推荐

1.2 创建项目

# 创建 React 项目
npm create vite@latest my-first-react-app -- --template react

# 进入项目 → 安装依赖 → 启动
cd my-first-react-app
npm install
npm run dev

浏览器打开 http://localhost:5173,看到欢迎页面就成功了!

1.3 项目结构

my-first-react-app/
├── src/             # 你写的代码都在这里
│   ├── App.tsx      # 根组件
│   ├── main.tsx     # 入口文件
│   └── index.css    # 全局样式
├── index.html       # HTML 模板
└── package.json     # 项目配置

💡 你只需要关心 src/ 目录,其他文件暂时不用管。


📚 二、核心思想:一切皆为组件

React 应用由一个个独立的、可复用的组件构成,就像搭积木:

App(根组件)
├── Header(头部)
├── Sidebar(侧边栏)
│   ├── NavItem
│   └── NavItem
└── Content(内容区)
    ├── Card
    └── Card

写一个最简单的组件

// Welcome.tsx — 组件就是一个函数,返回 JSX
function Welcome() {
  return <h1>你好,React!</h1>;
}

// App.tsx — 使用组件,像 HTML 标签一样
function App() {
  return (
    <div>
      <Welcome />   {/* 可以重复使用 */}
      <Welcome />
    </div>
  );
}

export default App;

关键规则:

  • 组件就是普通的 JavaScript 函数
  • 函数返回 JSX(看起来像 HTML,实际是 JavaScript)
  • 组件名必须大写开头(React 通过大小写区分组件和 HTML 标签)

📚 三、JSX 深度解析:React 的灵魂(重点)

JSX 是 React 最核心的概念,也是面试的高频考点。

3.1 JSX 到底是什么?

JSX = JavaScript XML,是一种 JavaScript 的语法扩展,让你可以在 JavaScript 里写"看起来像 HTML"的代码。

它不是 HTML! 虽然 JSX 看起来像 HTML,但它本质上是 JavaScript

// 你写的 JSX
const element = <h1 className="title">Hello, World!</h1>;

// 编译后的 JavaScript(Babel 转换)
const element = React.createElement('h1', { className: 'title' }, 'Hello, World!');

每一个 JSX 标签,最终都会被编译成 React.createElement() 函数调用。

这意味着:

  • JSX 不是模板引擎
  • JSX 不是字符串
  • JSX 是表达式,它会产生一个 JavaScript 对象(React Element)

3.2 为什么 React 选择 JSX?

对比维度传统模板(Vue / Angular)JSX
本质HTML 模板 + 指令JavaScript 表达式
逻辑处理需要 v-ifv-for 等指令直接用 JS 的 ifmap
类型检查模板层难以做 TS 检查天然支持 TypeScript
灵活性受限于模板语法完整的 JS 能力

💡 核心理念:React 团队认为 UI 逻辑和渲染逻辑天然耦合,不如把它们放在同一个地方——JavaScript。

3.3 JSX 的 9 条语法规则

规则 1:必须有一个根元素
// ❌ 错误:返回了两个并列的元素
function App() {
  return (
    <h1>标题</h1>
    <p>段落</p>
  );
}

// ✅ 正确:用 Fragment 避免多余的 DOM 节点
function App() {
  return (
    <>
      <h1>标题</h1>
      <p>段落</p>
    </>
  );
}

🔑 为什么? 因为 JSX 编译后是 React.createElement(),一个函数只能返回一个值。

规则 2:用 {} 嵌入 JavaScript 表达式

花括号 {} 是 JSX 的"入口",里面可以放任何 JavaScript 表达式

function Greeting() {
  const name = "小明";
  const isLoggedIn = true;

  return (
    <div>
      <h1>你好,{name}!</h1>                    {/* 变量 */}
      <p>2 + 3 = {2 + 3}</p>                      {/* 算术运算 */}
      <p>{isLoggedIn ? '已登录' : '请登录'}</p>     {/* 三元表达式 */}
      {isLoggedIn && <p>欢迎回来!</p>}             {/* 逻辑与 */}
    </div>
  );
}

注意:{} 里只能放表达式,不能放语句!

💡 什么是表达式 vs 语句?

表达式(✅ 可以放)语句(❌ 不能放)
count + 1if (count > 0) {}
isLoggedIn ? '是' : '否'for (let i = 0; i < 10; i++) {}
items.map(...)switch (type) {}
fn()let x = 1
规则 3:className 代替 class
// ❌ 错误:class 是 JavaScript 保留字
<div class="container">Hello</div>

// ✅ 正确:使用 className
<div className="container">Hello</div>

来看一个真实项目代码(使用 Tailwind CSS):

<div className="flex justify-center">
  <h1 className="text-3xl font-bold underline">你好,世界</h1>
</div>

💡 同样的道理:forhtmlFor(用在 <label> 上)

规则 4:style 接收对象,不是字符串
// ❌ 错误
<div style="color: red; font-size: 20px;">红色大字</div>

// ✅ 正确:style 是对象,属性名用驼峰
<div style={{ color: 'red', fontSize: '20px' }}>红色大字</div>

注意: 外层 {} 是 JSX 表达式入口,内层 {} 是 JavaScript 对象字面量。

规则 5:事件处理用驼峰命名
// ❌ 错误:HTML 风格
<button onclick="handleClick()">点我</button>

// ✅ 正确:React 风格,传入函数引用
<button onClick={handleClick}>点我</button>

// ✅ 箭头函数传参
<button onClick={() => handleDelete(item.id)}>删除</button>
HTML 事件React 事件
onclickonClick
onchangeonChange
onsubmitonSubmit
规则 6:必须闭合所有标签
// ✅ JSX 要求所有标签必须闭合
<img src="photo.jpg" />
<input type="text" />
<br />
规则 7:组件名必须大写开头
// ❌ 小写开头 → React 认为是 HTML 标签
function greeting() { return <h1>Hello</h1>; }

// ✅ 大写开头 → React 知道这是组件
function Greeting() { return <h1>Hello</h1>; }
规则 8:用 map 渲染列表,必须有 key
const todos = [
  { id: 1, text: '学习 JSX' },
  { id: 2, text: '写个项目' },
];

<ul>
  {todos.map(todo => (
    // key 必须唯一且稳定(不要用 index!)
    <li key={todo.id}>{todo.text}</li>
  ))}
</ul>

为什么需要 key 帮助 React 识别哪些元素改变了。用 index 作为 key 在列表增删时会导致 Bug。

规则 9:条件渲染的 3 种方式
// 方式 1:三元运算符(最常用)
<span>{status === 'active' ? '✅ 在线' : '❌ 离线'}</span>

// 方式 2:逻辑与(只渲染"真"的情况)
{isLoggedIn && <p>欢迎回来!</p>}

// 方式 3:提前 return(适合复杂逻辑)
if (status === 'loading') return <Spinner />;
if (status === 'error') return <ErrorMsg />;
return <Content />;

3.4 JSX 性能优化

很多人不知道,JSX 的写法直接影响渲染性能:

// ❌ 每次渲染都会创建新对象,导致子组件不必要的重渲染
<Child style={{ color: 'red' }} />

// ✅ 将对象提取到组件外部
const childStyle = { color: 'red' };
<Child style={childStyle} />

// ❌ 每次渲染都创建新的箭头函数
<button onClick={() => console.log('clicked')} />

// ✅ 提取为独立函数
const handleClick = useCallback(() => console.log('clicked'), []);
<button onClick={handleClick} />

💡 为什么? React 用 Object.is() 比较 Props。每次渲染创建新对象/函数,引用地址不同,React 就会认为 Props 变了,触发子组件重渲染。

3.5 JSX 与 Fiber 架构

React 16 引入了 Fiber 架构。你写的每一个 JSX 标签,编译后会经历这个链路:

JSX 标签 → React.createElement() → React Element → Fiber 节点 → 真实 DOM

Fiber 的核心思想是可中断的渲染

  • 旧架构:递归同步渲染整棵组件树,一旦开始就无法中断
  • 新架构:把渲染工作拆分成小单元(Fiber 节点),可以暂停、恢复

这意味着:JSX 写得越"可拆分"(组件化),React 的调度就越灵活。

3.6 JSX 速记卡片

概念一句话记忆代码示例
JSX 本质JS 的语法糖,编译成 createElement<div>React.createElement('div')
花括号JSX 的 JS 入口,只能放表达式{count}{a ? b : c}
classNameclass 的 React 版本className="box"
style接收对象,属性驼峰style={{ color: 'red' }}
组件大写开头的函数function App() {}
列表map + key(不要用 index){arr.map(i => <li key={i.id}>)}
条件渲染三元 / && / 提前return{ok ? <A/> : <B/>}
事件驼峰命名,传函数引用onClick={fn}
性能避免内联对象/函数提取到组件外或用 useMemo/useCallback

📚 四、常见踩坑清单

踩坑原因解决方案
class 报错JS 保留字className
style="color:red" 报错JSX 的 style 需要对象style={{ color: 'red' }}
if-else 在 JSX 里报错{} 里不能放语句用三元表达式或提前 return
for 循环在 JSX 里报错同上map()
<!-- 注释 --> 报错HTML 注释在 JSX 中无效{/* 注释 */}
返回两个并列元素报错没有根元素<>...</> 包裹
组件不渲染首字母没大写改成大写开头
列表渲染没有 keyReact 需要 key 来追踪元素添加唯一 key

💡 上篇总结

概念一句话理解
组件返回 JSX 的函数,大写开头
JSXJS 的语法糖,编译成 React.createElement(),花括号里只能放表达式
JSX 性能避免内联对象/函数,提取到组件外
FiberJSX → Element → Fiber → DOM,组件化让调度更灵活

📖 下篇预告

下一篇将讲解 React 的数据流和生命周期:

  • Props:父组件如何给子组件传数据(包括 TypeScript 写法)
  • StateuseState 的正确用法和函数式更新
  • useEffect:生命周期的终极答案(依赖数组、清理函数、竞态条件)
  • 事件处理 + 列表渲染
  • 实战:完整的待办事项 App

👉 关注我,下篇不迷路!


🔗 参考资料


💬 交流讨论

你在学 JSX 的过程中踩过什么坑?欢迎在评论区分享!

觉得有用?点个赞👍收藏⭐关注👆,下篇继续讲 React 生命周期和实战!