一、Vite 创建 React 项目
1.1 创建项目
npm create vite@latest my-app -- --template react
# TypeScript 项目用:--template react-ts
cd my-app
npm install
npm run dev # 启动开发服务器
npm run build # 生产构建
1.2 入口挂载(与 Vue 的差异)
// src/main.jsx —— React 用 createRoot 创建根节点
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
createRoot(document.getElementById("root")).render(<App />);
对比 Vue:Vue 是
createApp(App).mount("#app");React 是createRoot(...).render(...),本质都是"挂载组件树"。
二、项目目录结构解读
my-app/
├── index.html # 唯一 HTML,含 <div id="root">
├── vite.config.js # Vite 配置
├── public/ # 静态资源(原样拷贝)
└── src/
├── main.jsx # 入口:createRoot 渲染
├── App.jsx # 根组件
├── components/ # 组件目录
├── assets/ # 参与构建的资源
└── index.css # 全局样式
.jsx表示文件内含 JSX 语法;React 项目无模板文件,一切皆组件。
三、JSX 语法规则:与 HTML 的区别
JSX 是 JavaScript 的语法扩展,本质是 React.createElement() 的语法糖——在 JS 里写类似 HTML 的结构,但有几处关键差异:
| 对比项 | HTML | JSX |
|---|---|---|
| 类名 | class | className(class 是 JS 保留字) |
| 标签属性 | for | htmlFor(label 用) |
| 事件 | onclick="fn()" | onClick={fn}(驼峰 + 传函数) |
| 内联样式 | style="color:red" | style={{ color: "red" }}(对象 + 驼峰) |
| 标签闭合 | 部分可不闭合 | 必须闭合:<img />、<input /> |
| 注释 | <!-- --> | {/* ... */} |
export default function App() {
return (
<div className="app">
<h1>Hello React</h1>
{/* JSX 注释写法 */}
<button onClick={() => alert("点击")}>按钮</button>
<img src="/logo.png" alt="logo" style={{ width: 100 }} />
</div>
);
}
其他 JSX 规则
- 只能返回一个根节点(Vue3 支持多根,React 仍要求单根,可用 Fragment
<>...</>包裹); - JSX 表达式用
{ }包裹; - 属性值为字符串用引号,为表达式用
{}; - 变量命名采用驼峰。
四、变量与表达式渲染
4.1 渲染变量与表达式
const user = { name: "张三", age: 18 };
const isLogin = true;
export default function App() {
return (
<div>
<p>{user.name}</p> {/* 变量 */}
<p>{user.age + 1}</p> {/* 运算 */}
<p>{isLogin ? "已登录" : "未登录"}</p> {/* 三元 */}
<p>{formatDate(new Date())}</p> {/* 函数调用 */}
<p>{[1, 2, 3].join("-")}</p> {/* 方法调用 */}
</div>
);
}
function formatDate(d) {
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
}
4.2 渲染规则
{null}、{undefined}、{false}、{true}不渲染任何内容;{0}会渲染 "0"(数字 0 是有效内容)——条件渲染时注意。
五、条件渲染、列表渲染
5.1 条件渲染三种写法
// 写法一:&& 逻辑与(右侧为 false 时不渲染)
{isLogin && <p>欢迎回来</p>}
// 写法二:三元表达式
{isLogin ? <p>已登录</p> : <button onClick={goLogin}>去登录</button>}
// 写法三:提前 return(复杂分支)
function Panel({ status }) {
if (status === "loading") return <p>加载中…</p>;
if (status === "error") return <p>加载失败</p>;
return <p>加载完成</p>;
}
5.2 列表渲染(map + key)
const list = [
{ id: 1, name: "React" },
{ id: 2, name: "Vue" },
];
export default function App() {
return (
<ul>
{list.map((item) => (
<li key={item.id}>{item.name}</li> {/* key 必须且唯一 */}
))}
</ul>
);
}
key 的作用:帮助 React 识别节点复用与重建;不要用 index 做 key(插入/删除时渲染错位)。
六、JSX 常见报错踩坑
| 坑 | 现象 | 解决 |
|---|---|---|
用了 class 而不是 className | 样式不生效、控制台警告 | 全部改为 className |
| 标签未闭合 | 编译报错 | 单标签必须自闭合 <img /> |
| key 缺失/重复 | 控制台警告、列表更新错乱 | 加稳定唯一 key |
表达式用 {} 误写成字符串 | 渲染出字面量 | {count} 而非 "count" |
| style 传字符串 | 报错或不生效 | style={{ color: "red" }} 对象语法 |
| 多根节点 | 编译报错"Adjacent JSX elements" | 用 Fragment <>...</> 包裹 |
| 注释用 HTML 语法 | 显示为文本 | 用 {/* ... */} |
七、总结
- 搭建:Vite 模板 +
createRoot().render(),无模板文件、一切皆组件; - JSX:JS 里的类 HTML,
className、驼峰事件、{ }表达式、必须闭合; - 渲染:
{变量/表达式},null/undefined/false 不渲染; - 条件/列表:
&&、三元、提前 return;map+key; - 对比 Vue:模板指令(v-if/v-for)→ 纯 JS 表达式(&&、map);响应式 → 显式 setState;
- 踩坑:className、闭合、key、多根是四大高频报错。