创建 React 项目与第一个商品页面
本课程从一个商品页面开始,逐步学习组件、数据展示、事件、状态和表单,再通过接口请求数据,完成商品与分类管理。本节结束时,可以创建并启动 React 项目,在浏览器中显示第一张商品卡片,并通过修改代码更新页面内容。
开始前需要会什么
本课程面向第一次学习 React 的开发者,不要求学过 Vue 或其他前端框架。“零基础”指 React 开发零基础,开始前需要了解:
- HTML 的标题、段落、链接等基本标签。
- CSS 的选择器、颜色、字号和内外边距。
- JavaScript 的变量、对象、数组、函数和返回值。
如果只学过 Python,建议先补充 HTML、CSS 和 JavaScript 的基本用法。课程中首次用到解构、数组方法和异步请求时,会结合具体功能解释。
React、Vite 和 Node.js 各自负责什么
React是用于构建用户界面的 JavaScript 库。页面可以拆成多个组件,例如商品卡片、搜索框和新增商品表单。组件将相关的显示内容和交互逻辑组织在一起。
本节先编写一个名为 App 的组件,让它显示商品信息。后续再根据数据和用户操作改变页面。
Vite 提供开发服务器和构建工具。开发时,它将项目中的代码处理后交给浏览器;保存代码后,浏览器可以更新对应内容。课程使用 Vite 创建 React 项目。
Node.js让 JavaScript 可以在浏览器之外运行,本节用它运行 Vite 等开发工具。安装 Node.js 时会同时安装npm,用来下载项目依赖和执行项目命令。
安装开发工具
以下步骤以 Windows 10/11、VS Code 和 PowerShell 为例。
安装 Node.js
打开 Node.js 官方下载页,选择 Node.js 24 LTS 系列的 Windows 安装程序。普通 Intel/AMD 的 64 位电脑选择 x64;ARM 电脑选择 ARM64。下载 .msi 安装程序,按安装向导完成安装,保留 npm 和添加到 PATH 的选项。
PATH 是系统查找命令时使用的目录列表。将 Node.js 加入 PATH 后,才能直接在终端输入 node 和 npm。
本节示例在 Node.js 24.16.0、npm 11.13.0 下验证。Vite 要求 Node.js 20.19 以上,或 22.12 以上,详见 Vite 官方中文文档,Node.js 24 LTS 满足要求。已经安装 Node.js 的读者,先完成下一小节的版本检查,版本不满足要求时再重新安装。
安装 VS Code 并打开终端
从 VS Code 官方下载页(英文) 下载 Windows 安装程序并完成安装。React 项目需要编辑 .jsx、.js 和 .css 文件,VS Code 已提供这些文件的基本支持。
安装 Node.js 后,重新打开 VS Code,让新进程读取更新后的 PATH。
按 Ctrl+Shift+P 打开命令面板,输入 Terminal: Select Default Profile,选择 PowerShell。再通过菜单 Terminal > New Terminal(终端 > 新建终端)打开终端。
在终端逐条执行:
node --version
npm --version本节验证环境中的输出分别为:
v24.16.0
11.13.0小版本不同可能显示其他数字,关键是两条命令都能正常输出版本。
如果提示无法识别 node 或 npm,先关闭并重新打开 VS Code;仍然失败时,重新运行 Node.js 安装程序,确认安装了 npm 并添加到 PATH。
创建 React 项目
准备项目所在文件夹
在资源管理器中新建一个存放课程工程的文件夹,例如 D:\Course。如果电脑没有 D 盘,也可以在其他磁盘创建 Course 文件夹。后续路径以实际选择的位置为准。
在 VS Code 中选择 File > Open Folder(文件 > 打开文件夹),打开这个文件夹。
通过 Terminal > New Terminal 新建终端,确认提示符中的当前目录为 D:\Course。如果使用了其他位置,以实际打开的文件夹为准。
这里先打开的是项目的上一级目录,下一条命令会在其中新建 react-demo。
生成项目文件
在终端执行:
npx create-vite@9.2.1 react-demo --template react --no-interactive第一次使用时,npx 可能询问是否安装 create-vite@9.2.1,输入 y 并回车。命令会联网下载项目创建工具。
按顺序理解命令中的内容:
npx create-vite@9.2.1:运行指定版本的create-vite项目创建工具。react-demo:新建项目的文件夹名称。--template react:选择 React 的 JavaScript 模板。--no-interactive:使用命令中指定的选项创建项目,不进入模板选择问答。
这里的 9.2.1 是项目创建工具的版本,和 React、Vite 本身的版本不同。
本节没有使用 npm create vite@9.2.1 react-demo -- --template react --no-interactive 这种常见写法。在 PowerShell 7.6.0 中实测,这条命令后半部分的选项没有传给创建工具,终端仍然进入“Select a framework”问答。如果执行后出现这个选择界面,按 Ctrl+C 退出,再执行上面的 npx 命令。
创建完成后,左侧会出现 react-demo 文件夹,终端会提示进入项目、安装依赖和启动服务。接下来按下面的步骤打开项目,再执行安装与启动命令。
如果当前目录已经有同名的 react-demo,先确认它是否就是此前创建的课程项目。已有项目可直接打开继续,不要在包含自己文件的目录中反复创建或选择清空目录。
打开项目并安装依赖
在 VS Code 中再次选择 File > Open Folder,打开刚生成的 D:\Course\react-demo。左侧资源管理器的顶层文件夹应变成 react-demo。
新建终端,确认当前目录为 react-demo,并能在左侧看到 package.json。后续命令都在这个项目根目录执行。
执行:
npm install依赖是项目需要使用的第三方代码,例如 React 和 Vite。package.json 记录这个项目需要哪些依赖,npm install 根据它下载依赖。
安装完成后会生成 node_modules 目录和 package-lock.json 文件。前者保存下载后的依赖,后者记录实际安装的完整版本,应与项目代码一起保留。node_modules 中的文件由 npm 管理,不需要手动修改。
本节验证时使用 React 19.3.1、Vite 8.3.4。模板中的版本范围可能允许安装后续兼容版本,以项目生成的 package-lock.json 为准。
安装时保持网络连接,等待命令执行完成、终端重新出现可输入命令的提示符,再继续下一步。
启动并打开页面
在 react-demo 项目根目录执行:
npm run devpackage.json 的 scripts 区域为常用命令起名字,npm run dev 就是执行其中名为 dev 的命令。当前模板中,dev 对应 vite,作用是启动开发服务器。
默认使用 5173 端口,启动成功后,终端的 Local 一行通常显示:
http://localhost:5173/打开 Chrome 或 Edge,在地址栏输入终端显示的 Local 地址。当前模板会显示带有 Vite、React 标识和计数按钮的初始页面,说明开发服务器已启动,浏览器已经加载 React 应用。

localhost 表示当前电脑,端口号用于区分本机正在运行的不同服务。默认端口被占用时,Vite 会尝试其他端口,以终端实际显示的地址为准。不要双击 index.html,应通过开发服务器地址访问项目。
服务运行期间,这个终端会一直显示运行信息,不会回到普通命令提示符,这是正常现象。暂时保留它,接下来可以直接编辑文件。
后面排查问题会用到浏览器的开发者工具:在页面上按 F12,切换到 Console(控制台)标签,代码运行出错时,详细信息会显示在这里。
修改第一个组件
先在左侧资源管理器展开 src 文件夹。本节会修改其中的 App.jsx、index.css 和 App.css:
| 文件 | 用途 |
|---|---|
src/App.jsx |
编写要显示的商品界面 |
src/index.css |
设置整个页面的通用样式 |
src/App.css |
设置商品页面和卡片的样式 |
这些文件已经由模板生成,直接打开修改即可。正文中的 src/App.jsx 表示“项目根目录下 src 文件夹中的 App.jsx”,文件名和大小写需要保持一致。
先让页面显示一句话
在左侧资源管理器打开 src/App.jsx,将文件全部内容替换为:
export default function App() {
return <h1>商品管理</h1>
}按 Ctrl+S 保存,再回到浏览器。页面会变成“商品管理”,原来的标识和计数按钮消失。此时仍可能沿用模板的背景和居中样式,后面会统一调整。
App 是一个 JavaScript 函数,返回要显示的内容。React 将这样的函数作为组件使用:
function App():声明名为App的函数,React 组件名称以大写字母开头。return:返回当前组件的界面内容。<h1>商品管理</h1>:返回一级标题。export default:将App作为当前文件的默认导出,供其他文件导入。
写在 JavaScript 中、形似 HTML 标签的语法叫 JSX。Vite 会处理它,让浏览器能够执行。.jsx 后缀帮助开发工具识别包含 JSX 的 JavaScript 文件。
后续再展开 JSX 的具体规则。本节先使用正确闭合的标签,通过 return 返回组件要显示的内容。可以结合 React 官方中文快速入门 查阅组件和 JSX 的基本写法。
代码中的两个空格表示缩进,用于看清哪些内容属于函数内部。后面出现嵌套标签时,也会用缩进表示层次。
用 main 包住页面主体
接下来要在标题下面加入商品卡片,先为这些内容准备一个共同的外层容器。
main 是 HTML 标签,表示页面的主要内容区域。商品管理标题和商品卡片都属于本页主体,所以放在 <main>...</main> 中。以后页面加入导航栏或页脚时,这些公共区域通常放在 main 外面。一个页面通常只保留一个可见的 main,便于屏幕阅读器定位主体内容。
React 没有要求最外层必须使用 main,通用容器 div 也能包住这些内容。选择 main 是为了说明“这是页面主体”。这种用标签表达内容用途的写法,称为 HTML 语义化。
将 src/App.jsx 的全部内容替换为:
export default function App() {
return (
<main className="page">
<h1>商品管理</h1>
</main>
)
}return 后面的括号将多行 JSX 组织在一起,左括号与 return 写在同一行。最外层的 main 包住页面内容,后面新增的商品卡片也放在它里面。
className 为标签指定 CSS 类名,作用对应 HTML 中的 class。这里的 page 是自行起的类名,稍后通过 .page 选择器设置样式。main 和类名本身不会自动生成背景或居中效果。
保存后,页面仍然只显示“商品管理”。这一步增加的是页面结构,还没有增加新的可见文字。
显示第一张商品卡片
在 h1 后面加入一张商品卡片。卡片使用 article 标签,表示一块可以独立理解的内容;这里的名称、价格、库存和上架状态构成完整的商品信息。
卡片中的 h2 是二级标题,用作商品名称;p 是段落,用来显示每一项商品资料。页面的 h1 表示“商品管理”,卡片中的 h2 表示其中的一件商品,两者通过标题级别表达层次。
下面是修改后的完整 src/App.jsx,将文件全部内容替换为它:
import './App.css'
export default function App() {
return (
<main className="page">
<h1>商品管理</h1>
<article className="product-card">
<h2>矿泉水</h2>
<p>价格:¥3.50</p>
<p>库存:100 件</p>
<p>上架状态:已上架</p>
</article>
</main>
)
}import './App.css' 加载当前目录中的样式文件。路径中的 ./ 表示当前文件所在目录,这里对应 src/App.css。
product-card 也是自行起的类名,用来单独设置卡片样式。查看 JSX 的缩进可以看出:标题和卡片都在 main 里面,商品名称和三个段落又在 article 里面。
保存后先核对新增的文字是否出现。此时仍在使用模板的样式,背景、文字颜色和间距会在下一步统一调整。
现在的商品名称、价格和库存都是固定文字。修改源文件并保存才会改变它们;通过输入框或按钮更新商品,留到后续讲到事件与状态时再介绍。
替换模板样式
打开 src/index.css,将全部内容替换为:
* {
box-sizing: border-box;
}
body {
margin: 0;
min-width: 320px;
background: #f5f7fa;
color: #1f2937;
font-family: Arial, "Microsoft YaHei", sans-serif;
line-height: 1.6;
}index.css 提供页面通用样式。* 选择所有元素,box-sizing: border-box 让元素设置的宽度包含内边距与边框。body 是 HTML 中承载页面内容的元素,这里为它去掉默认外边距,并设置背景、文字颜色和字体。
min-width: 320px 作为本例页面的最小宽度,line-height: 1.6 将行高设为字号的 1.6 倍,让文字之间留出空间。
再打开 src/App.css,将全部内容替换为:
.page {
max-width: 800px;
margin: 0 auto;
padding: 32px 16px;
}
.page h1 {
margin: 0 0 24px;
font-size: 28px;
}
.product-card {
padding: 24px;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: #ffffff;
}
.product-card h2 {
margin: 0 0 16px;
font-size: 22px;
}
.product-card p {
margin: 8px 0;
}.page 对应 className="page",限制内容区域宽度,并通过 margin: 0 auto 居中。.product-card 对应商品卡片,设置内边距、边框和白色背景。
.product-card h2 选择卡片内部的二级标题,.product-card p 选择卡片内部的段落。padding 设置内容与边框之间的距离,margin 调整元素外部的间距。
保存后,页面应显示浅灰背景、一个“商品管理”标题和一张白色商品卡片。卡片内容为:
矿泉水
价格:¥3.50
库存:100 件
上架状态:已上架
只删除 App.jsx 中的模板内容不会去掉 index.css 的全局样式,因此两个 CSS 文件都需要按本节替换。
App 怎样显示到浏览器中
完成第一个页面后,再看项目入口。当前需要认识的文件如下,其他生成文件可以先保留:
react-demo/
├── index.html
├── package.json
├── package-lock.json
├── vite.config.js
├── node_modules/
├── public/
└── src/
├── assets/
├── main.jsx
├── App.jsx
├── index.css
└── App.css模板还带有 public 与 src/assets,存放模板自带的图标等文件,本节先保留原样。
vite.config.js 是 Vite 的配置文件,模板已经配置了 React 插件,本节保留原样。main.jsx 和 index.html 决定应用从哪里加载,下面查看它们怎样使用刚才编写的 App。
项目根目录的 index.html 是浏览器加载的 HTML 入口。其中包含一个容器和一个模块脚本:
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>这里摘出了已有文件中的两行用于观察,不需要将整个 index.html 替换为这两行。id="root" 标记 React 要使用的容器,script 加载 src/main.jsx;type="module" 表示以 JavaScript 模块方式加载,允许代码通过 import 引入其他模块。
打开 src/main.jsx,模板中已经有以下完整代码,本节保留原样:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)渲染就是让 React 根据组件描述的内容生成或更新页面。这段入口代码只需要认识三处:
import './index.css'加载全局样式,import App from './App.jsx'导入刚才编写的组件。createRoot(document.getElementById('root')).render(...)找到index.html中 ID 为root的容器,把<App />显示在里面。<App />表示使用App组件,/>是没有子内容时的自闭合写法。StrictMode在开发时启用额外检查,不会生成可见标签。本节保留模板写法,不需要修改。
{ createRoot } 的大括号表示按名称导入,import App 没有大括号,对应 App.jsx 中的默认导出。createRoot 和 StrictMode 的更多细节暂不展开,用到时再讲。
flowchart TD
HTML["index.html:提供 root 容器"] --> ENTRY["main.jsx:找到容器,渲染 App"]
ENTRY -->|导入组件| APP["App.jsx:定义商品界面"]
ENTRY -->|加载全局样式| GLOBAL["index.css:全局样式"]
APP -->|加载页面样式| CARD["App.css:商品页面样式"]
ENTRY -->|将 App 渲染到 root| VIEW["浏览器:显示商品卡片"]
classDef entry fill:#dbeafe,stroke:#2563eb,color:#1e3a8a;
classDef component fill:#ede9fe,stroke:#7c3aed,color:#4c1d95;
classDef stylesheet fill:#fef3c7,stroke:#d97706,color:#78350f;
classDef view fill:#dcfce7,stroke:#16a34a,color:#14532d;
class HTML,ENTRY entry;
class APP component;
class GLOBAL,CARD stylesheet;
class VIEW view;从 index.html 找到 main.jsx,再沿箭头查看它导入的组件和样式。main.jsx 最后调用渲染方法,将 App 的界面显示在 root 容器中。编辑商品内容时主要修改 App.jsx,不需要每次改动入口文件。
连续核对页面更新
保持开发服务运行,在 src/App.jsx 中将“矿泉水”改成“可乐”,将 ¥3.50 改成 ¥5.00,按 Ctrl+S 保存。
回到浏览器,不手动刷新,确认商品名和价格已更新。Vite 监听到文件变化后,会将更新交给浏览器,这称为热更新。
核对完成后,将名称和价格恢复为“矿泉水”和 ¥3.50,保存,保留课程统一的初始商品。
然后回到运行服务的终端,按 Ctrl+C 停止服务;如果终端询问是否终止批处理,输入 Y 并回车。停止后刷新浏览器,页面将无法重新连接开发服务器。
仍在项目根目录重新执行:
npm run dev看到 Local 地址后重新加载页面,商品卡片再次显示。源文件保存在磁盘上,不会因停止开发服务而丢失。
常见问题
提示找不到 package.json 或 dev 脚本
执行 npm run dev 出现下面两种提示时,先看终端中的当前目录。常见原因是仍处于 D:\Course,而项目实际位于它下面的 react-demo。npm 会从当前目录开始,向上级目录查找 package.json,所以提示的内容取决于上级目录里有没有这个文件:
- 当前目录和所有上级目录都没有
package.json:提示中有Could not read package.json,错误码是ENOENT,意思是找不到package.json。 - 上级目录里恰好有一个
package.json,但其中没有dev脚本:提示Missing script: "dev"。
这两种提示都说明当前目录不是项目根目录。
通过 VS Code 打开 react-demo 文件夹,再新建终端,确认当前目录的 package.json 中有 "dev": "vite" 后重试。安装依赖前同样需要确认当前目录:在没有项目的目录中执行 npm install,可能同样提示 Could not read package.json;如果上级目录恰好有 package.json,npm 会把它当作当前项目,命令不报错,但依赖装进了那个上级项目,课程项目的依赖并没有安装。
启动后地址不是 5173 端口
默认端口被占用时,Vite 会自动尝试其他端口,例如 5174。直接打开本次启动后终端显示的 Local 地址即可。
若此前已在另一个终端启动同一项目,可以继续使用那个服务,或回到对应终端按 Ctrl+C 停止,避免重复启动。
修改后仍显示原来的页面
先确认 App.jsx 已保存,VS Code 标签上没有表示未保存的小圆点。再核对浏览器地址与当前终端的 Local 地址一致,避免打开另一个项目的服务。
如果旧内容已经消失,只是背景或布局仍像模板,检查 index.css 和 App.css 是否都已完整替换,以及 App.jsx 顶部是否保留 import './App.css'。
页面显示 JSX 错误
如果编辑时漏写结束标签,或误用中文引号,Vite 会在浏览器页面上盖一层错误提示,终端里也会输出同样的信息。先看提示中的文件路径与行号,再对照本节完整代码修正对应位置,保存后页面通常会自动恢复,没有恢复时按 F5 刷新。
例如 <h1>商品管理</h1> 的开始与结束标签需要配对;className="page" 中使用英文引号。出错后不要先重建项目,先修复提示定位到的源文件。
安装依赖时出现网络错误
如果安装命令因网络超时或连接失败而退出,先确认电脑可以访问网络,再在同一个 react-demo 根目录重新执行失败的安装命令。安装尚未成功时,直接启动可能提示找不到 Vite;先完成依赖安装再启动。
完成本节后,项目应能启动、显示矿泉水商品卡片,并在保存修改后更新内容。后面继续在这个项目中使用 JSX,将固定文字改为从 JavaScript 数据中读取。