当前位置: 首页 > news >正文

React 快速入门:菜谱应用实战教程

React 快速入门:菜谱应用实战教程

第一部分:React 开发准备

1.1 React 是什么?

React 是 Facebook 开发的一个用于构建用户界面的 JavaScript 库。它具有三个核心特点:

组件化(Component-Based)
将复杂的 UI 拆分成独立、可复用的组件,就像搭积木一样构建应用。

声明式编程(Declarative)
你只需要描述 UI 应该"是什么样子",React 会自动处理 DOM 更新。

单向数据流(Unidirectional Data Flow)
数据从父组件流向子组件,让数据流动可预测、易调试。

为什么需要构建工具?

直接在浏览器中使用 React 会遇到三个问题:

  1. JSX 语法:浏览器不认识 <div>Hello</div> 这样的 JSX 代码
  2. 模块化:无法使用 import/export 组织代码
  3. 现代 JavaScript:ES6+ 语法在旧浏览器中不兼容

这就需要 Webpack 和 Babel 这两个工具来解决。


1.2 开发环境准备

前置要求

确保已安装 Node.js(推荐 14.x 或更高版本)。验证安装:

node -v
npm -v

创建项目目录

mkdir recipes-app
cd recipes-app

初始化项目并安装依赖

# 初始化 package.json
npm init -y# 安装 React 核心库
npm install react react-dom# 安装 Webpack 打包工具
npm install -D webpack webpack-cli# 安装 Babel 转译工具
npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react

依赖说明

依赖包作用
reactReact 核心库
react-domReact DOM 操作库
webpack模块打包器
webpack-cliWebpack 命令行工具
babel-loaderWebpack 的 Babel 加载器
@babel/coreBabel 核心库
@babel/preset-env转译 ES6+ 语法
@babel/preset-react转译 JSX 语法

1.3 项目结构搭建

目标结构

recipes-app/
├── package.json
├── .babelrc
├── webpack.config.js
├── dist/
│   └── index.html
└── src/├── index.js├── components/│   ├── Menu.js│   ├── Recipe.js│   ├── IngredientsList.js│   ├── Ingredient.js│   └── Instructions.js└── data/└── recipes.json

创建目录和文件

Windows (CMD/PowerShell):

mkdir dist
mkdir src
mkdir src\components
mkdir src\datatype nul > .babelrc
type nul > webpack.config.js
type nul > dist\index.html
type nul > src\index.js
type nul > src\components\Menu.js
type nul > src\components\Recipe.js
type nul > src\components\IngredientsList.js
type nul > src\components\Ingredient.js
type nul > src\components\Instructions.js
type nul > src\data\recipes.json

macOS/Linux:

mkdir -p dist src/components src/datatouch .babelrc webpack.config.js
touch dist/index.html
touch src/index.js
touch src/components/{Menu,Recipe,IngredientsList,Ingredient,Instructions}.js
touch src/data/recipes.json

跨平台(Node.js):

node -e "const fs=require('fs');const dirs=['dist','src','src/components','src/data'];dirs.forEach(d=>fs.mkdirSync(d,{recursive:true}));const files=['.babelrc','webpack.config.js','dist/index.html','src/index.js','src/components/Menu.js','src/components/Recipe.js','src/components/IngredientsList.js','src/components/Ingredient.js','src/components/Instructions.js','src/data/recipes.json'];files.forEach(f=>fs.writeFileSync(f,''));"

第二部分:配置构建工具

2.1 Babel 配置(.babelrc)

创建 .babelrc 文件,内容如下:

{"presets": ["@babel/preset-env", "@babel/preset-react"]
}

配置说明

  • @babel/preset-env:将 ES6+ 语法(箭头函数、解构、模板字符串等)转译为 ES5
  • @babel/preset-react:将 JSX 语法转译为 React.createElement() 函数调用

Babel 的作用

Babel 是一个 JavaScript 编译器,它让你能使用最新的语法编写代码,然后自动转换成浏览器能理解的旧版本代码。

转译示例:

// 你写的代码
const greeting = (name) => <h1>Hello, {name}!</h1>;// Babel 转译后
var greeting = function(name) {return React.createElement("h1", null, "Hello, ", name, "!");
};

2.2 Webpack 配置(webpack.config.js)

创建 webpack.config.js 文件:

const path = require('path');module.exports = {entry: './src/index.js',output: {path: path.join(__dirname, 'dist', 'assets'),filename: 'bundle.js'},module: {rules: [{test: /\.jsx?$/,exclude: /node_modules/,loader: 'babel-loader'}]},devtool: 'source-map'
};

配置说明

配置项作用
entry应用的入口文件,Webpack 从这里开始构建依赖图
output.path打包后文件的输出目录
output.filename打包后文件的名称
module.rules定义如何处理不同类型的文件
test: /\.jsx?$/匹配所有 .js 和 .jsx 文件
loader: 'babel-loader'使用 Babel 转译 JS/JSX 文件
devtool: 'source-map'生成源码映射,便于浏览器调试

Webpack 的作用

Webpack 是一个模块打包器。它的工作流程:

  1. entry 入口文件开始
  2. 分析所有的 import 语句,构建依赖图
  3. 使用对应的 loader 处理每种类型的文件
  4. 将所有模块打包成一个或多个 bundle 文件
src/index.js (入口)↓ import Menu
src/components/Menu.js↓ import Recipe
src/components/Recipe.js↓ import IngredientsList, Instructions
...↓ 打包
dist/assets/bundle.js (输出)

2.3 添加构建脚本(package.json)

package.json 中添加 scripts 字段:

{"name": "recipes-app","version": "1.0.0","scripts": {"dev": "webpack --mode development","build": "webpack --mode production"},"dependencies": {"react": "^18.2.0","react-dom": "^18.2.0"},"devDependencies": {"@babel/core": "^7.23.0","@babel/preset-env": "^7.23.0","@babel/preset-react": "^7.22.0","babel-loader": "^9.1.3","webpack": "^5.89.0","webpack-cli": "^5.1.4"}
}

脚本说明

  • npm run dev:开发模式构建(代码未压缩,构建快)
  • npm run build:生产模式构建(代码压缩,体积小)

2.4 HTML 入口页面(dist/index.html)

创建 dist/index.html 文件:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>React 菜谱应用</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Arial', sans-serif;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;padding: 20px;}#root {max-width: 1200px;margin: 0 auto;}article > header {text-align: center;margin-bottom: 40px;}article > header h1 {color: white;font-size: 3rem;text-shadow: 2px 2px 4px rgba(0,0,0,0.3);margin-bottom: 10px;}.recipes {display: grid;grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));gap: 30px;}.recipe {background: white;border-radius: 16px;padding: 30px;box-shadow: 0 10px 30px rgba(0,0,0,0.2);transition: transform 0.3s ease;}.recipe:hover {transform: translateY(-5px);}.recipe h2 {color: #667eea;font-size: 2rem;margin-bottom: 20px;border-bottom: 3px solid #667eea;padding-bottom: 10px;}.ingredients {background: #f8f9fa;border-radius: 12px;padding: 20px;margin-bottom: 25px;border-left: 5px solid #667eea;}.ingredients li {list-style: none;padding: 8px 0;color: #495057;font-size: 1.05rem;}.ingredients li:before {content: "✓ ";color: #667eea;font-weight: bold;margin-right: 8px;}.instructions {margin-top: 20px;}.instructions h3 {color: #495057;font-size: 1.3rem;margin-bottom: 15px;}.instructions ol {padding-left: 25px;}.instructions li {margin: 12px 0;line-height: 1.6;color: #6c757d;font-size: 1.05rem;}.instructions li:before {font-weight: bold;color: #667eea;}</style>
</head>
<body><div id="root"></div><script src="assets/bundle.js"></script>
</body>
</html>

关键点说明

  • <div id="root"></div>:React 应用的挂载点
  • <script src="assets/bundle.js"></script>:引入 Webpack 打包后的文件
  • CSS 样式:为菜谱应用提供美观的视觉效果

第三部分:React 菜谱应用实战

3.1 React 基础概念

在开始编写组件之前,我们需要理解两个核心概念:

JSX 语法

JSX 是 JavaScript 的语法扩展,让你能在 JavaScript 中编写类似 HTML 的代码。

JSX 基本规则:

// 1. JSX 必须有一个根元素
function App() {return (<div><h1>标题</h1><p>段落</p></div>);
}// 2. 在 JSX 中使用 JavaScript 表达式(用 {} 包裹)
function Greeting({ name }) {return <h1>Hello, {name}!</h1>;
}// 3. 使用 className 代替 class(因为 class 是 JS 关键字)
function Button() {return <button className="btn-primary">点击</button>;
}// 4. 自闭合标签必须有 /
function Image() {return <img src="photo.jpg" alt="照片" />;
}// 5. 可以在 JSX 中使用 map 渲染列表
function List({ items }) {return (<ul>{items.map((item, index) => (<li key={index}>{item}</li>))}</ul>);
}
Props(属性)

Props 是父组件向子组件传递数据的方式,类似于函数的参数。

Props 的特点:

  1. 只读:子组件不能修改 props
  2. 单向流动:数据从父组件流向子组件
  3. 任意类型:可以传递字符串、数字、对象、数组、函数等
// 父组件传递 props
function Parent() {return <Child name="张三" age={25} />;
}// 子组件接收 props(方式一:对象解构)
function Child({ name, age }) {return <p>{name} 今年 {age} 岁</p>;
}// 子组件接收 props(方式二:props 对象)
function Child(props) {return <p>{props.name} 今年 {props.age} 岁</p>;
}

3.2 应用需求分析

我们要构建一个菜谱应用,具有以下功能:

功能需求

  • 展示多道菜谱
  • 每道菜谱包含:菜名、配料列表、烹饪步骤

数据结构设计

创建 src/data/recipes.json 文件:

[{"name": "意大利面","ingredients": [{ "name": "意大利面", "amount": 200, "measurement": "克" },{ "name": "番茄酱", "amount": 100, "measurement": "克" },{ "name": "洋葱", "amount": 1, "measurement": "个" },{ "name": "大蒜", "amount": 3, "measurement": "瓣" },{ "name": "橄榄油", "amount": 2, "measurement": "汤匙" }],"steps": ["煮沸一锅盐水","加入意大利面煮 8-10 分钟","热锅加橄榄油,爆香蒜末和洋葱丁","加入番茄酱翻炒均匀","将煮好的面条加入酱汁中拌匀","装盘即可享用"]},{"name": "炒饭","ingredients": [{ "name": "米饭", "amount": 2, "measurement": "碗" },{ "name": "鸡蛋", "amount": 2, "measurement": "个" },{ "name": "胡萝卜", "amount": 50, "measurement": "克" },{ "name": "青豆", "amount": 30, "measurement": "克" },{ "name": "酱油", "amount": 1, "measurement": "汤匙" }],"steps": ["鸡蛋打散炒熟后盛出","胡萝卜切丁,与青豆一起炒熟","加入米饭翻炒","加入炒好的鸡蛋","倒入酱油调味","翻炒均匀后出锅"]},{"name": "番茄炒蛋","ingredients": [{ "name": "番茄", "amount": 3, "measurement": "个" },{ "name": "鸡蛋", "amount": 4, "measurement": "个" },{ "name": "白糖", "amount": 1, "measurement": "勺" },{ "name": "盐", "amount": 1, "measurement": "勺" },{ "name": "食用油", "amount": 2, "measurement": "汤匙" }],"steps": ["番茄切块,鸡蛋打散","热锅下油,炒鸡蛋至半熟盛出","再下油,炒番茄块至出汁","加入白糖和盐调味","倒入炒好的鸡蛋","翻炒均匀后出锅"]}
]

3.3 组件设计思路

我们采用自底向上的方式设计组件,从最小的组件开始构建:

Menu(菜单)└─ Recipe(单个菜谱)├─ IngredientsList(配料列表)│   └─ Ingredient(单个配料)└─ Instructions(步骤说明)

组件职责划分:

组件职责Props
Ingredient显示单个配料amount, measurement, name
IngredientsList渲染配料列表list
Instructions显示烹饪步骤title, steps
Recipe组合配料和步骤name, ingredients, steps
Menu渲染多个菜谱recipes

3.4 编写基础组件

3.4.1 Ingredient 组件

创建 src/components/Ingredient.js

import React from 'react';function Ingredient({ amount, measurement, name }) {return (<li>{amount} {measurement} {name}</li>);
}export default Ingredient;

组件说明:

  • 职责:显示单个配料,格式为"数量 单位 名称"
  • Props
    • amount:配料数量
    • measurement:计量单位
    • name:配料名称
  • 返回:一个 <li> 元素

3.4.2 IngredientsList 组件

创建 src/components/IngredientsList.js

import React from 'react';
import Ingredient from './Ingredient';function IngredientsList({ list }) {return (<ul className="ingredients">{list.map((ingredient, i) => (<Ingredient key={i} {...ingredient} />))}</ul>);
}export default IngredientsList;

组件说明:

  • 职责:循环渲染配料列表
  • Props
    • list:配料数组
  • 关键技术
    • 使用 map 方法遍历数组
    • key={i}:React 要求列表项必须有唯一的 key
    • {...ingredient}:展开运算符,等价于 amount={ingredient.amount} measurement={ingredient.measurement} name={ingredient.name}

3.4.3 Instructions 组件

创建 src/components/Instructions.js

import React from 'react';function Instructions({ title, steps }) {return (<section className="instructions"><h3>{title}</h3><ol>{steps.map((step, i) => (<li key={i}>{step}</li>))}</ol></section>);
}export default Instructions;

组件说明:

  • 职责:显示烹饪步骤说明
  • Props
    • title:步骤标题
    • steps:步骤数组
  • 返回:带有标题和有序列表的 section

3.5 组合组件

3.5.1 Recipe 组件

创建 src/components/Recipe.js

import React from 'react';
import IngredientsList from './IngredientsList';
import Instructions from './Instructions';function Recipe({ name, ingredients, steps }) {return (<section className="recipe"><h2>{name}</h2><IngredientsList list={ingredients} /><Instructions title="烹饪步骤" steps={steps} /></section>);
}export default Recipe;

组件说明:

  • 职责:组合配料列表和烹饪步骤,展示完整的单道菜谱
  • Props
    • name:菜名
    • ingredients:配料数组
    • steps:步骤数组
  • 组合方式:使用 <IngredientsList><Instructions> 子组件

3.5.2 Menu 组件

创建 src/components/Menu.js

import React from 'react';
import Recipe from './Recipe';function Menu({ recipes }) {return (<article><header><h1>美味菜谱</h1></header><div className="recipes">{recipes.map((recipe, i) => (<Recipe key={i} {...recipe} />))}</div></article>);
}export default Menu;

组件说明:

  • 职责:应用的根组件,渲染所有菜谱
  • Props
    • recipes:菜谱数组
  • 结构:包含标题和多个 Recipe 组件

3.6 应用入口

创建 src/index.js

import React from 'react';
import { createRoot } from 'react-dom/client';
import Menu from './components/Menu';
import data from './data/recipes.json';const root = createRoot(document.getElementById('root'));
root.render(<Menu recipes={data} />);

代码说明:

  1. 导入依赖

    • React:React 核心库
    • createRoot:React 18 的新 API,用于创建根节点
    • Menu:我们的根组件
    • data:菜谱数据
  2. 创建根节点

    const root = createRoot(document.getElementById('root'));
    

    获取 HTML 中的 <div id="root"> 元素并创建 React 根节点

  3. 渲染应用

    root.render(<Menu recipes={data} />);
    

    将 Menu 组件渲染到根节点,并传入菜谱数据


第四部分:构建与运行应用

4.1 开发模式构建

在项目根目录运行:

npm run dev

构建过程:

  1. Webpack 读取 src/index.js 入口文件
  2. 分析所有 import 语句,构建依赖图
  3. 使用 babel-loader 转译 JSX 和 ES6+ 语法
  4. 打包所有模块到 dist/assets/bundle.js

生成的文件:

dist/assets/
├── bundle.js              # 应用代码(未压缩,约 1.2 MB)
├── bundle.js.map          # 源码映射文件
└── bundle.js.LICENSE.txt  # 第三方库许可证

文件说明:

  • bundle.js:包含你的代码和 React 库的完整应用
  • bundle.js.map:Source Map 文件,用于浏览器调试
  • bundle.js.LICENSE.txt:React 等第三方库的开源许可证信息

4.2 在浏览器中查看

用浏览器打开 dist/index.html,你会看到:

  • 页面标题:“美味菜谱”
  • 三道菜谱卡片(意大利面、炒饭、番茄炒蛋)
  • 每个卡片包含配料和步骤

使用开发者工具调试:

  1. F12 打开开发者工具
  2. 切换到 Sources 面板
  3. 在左侧文件树中找到 webpack://src/ 目录
  4. 可以看到你的原始源代码(这就是 Source Map 的作用)
  5. 设置断点并调试

Source Map 的价值:

没有 Source Map,你只能看到压缩后的 bundle.js

!function(e){var t={};function n(r){if(t[r])return...

有了 Source Map,你可以直接调试源代码:

function Ingredient({ amount, measurement, name }) {return <li>{amount} {measurement} {name}</li>;
}

4.3 生产模式构建

准备部署时,使用生产模式:

npm run build

与开发模式的区别:

特性开发模式生产模式
代码压缩
混淆变量名
移除注释
文件大小~1.2 MB~150 KB
构建速度
调试体验
适用场景本地开发线上部署

生产模式的代码示例:

// 开发模式(可读)
function Ingredient(_ref) {var amount = _ref.amount,measurement = _ref.measurement,name = _ref.name;return /*#__PURE__*/ (0, _jsxRuntime.jsxs)("li", {children: [amount, " ", measurement, " ", name]});
}// 生产模式(压缩混淆)
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t()...

使用场景:

  • 开发时:npm run dev(快速迭代)
  • 上线前:npm run build(优化性能)

第五部分:动手实践

5.1 练习任务

练习 1:添加新菜谱 ⭐

任务描述:
recipes.json 中添加一道你喜欢的菜,重新构建并查看效果。

操作步骤:

  1. 打开 src/data/recipes.json
  2. 在数组中添加新对象:
{"name": "宫保鸡丁","ingredients": [{ "name": "鸡胸肉", "amount": 300, "measurement": "克" },{ "name": "花生米", "amount": 100, "measurement": "克" },{ "name": "干辣椒", "amount": 10, "measurement": "个" },{ "name": "花椒", "amount": 1, "measurement": "勺" },{ "name": "酱油", "amount": 2, "measurement": "汤匙" }],"steps": ["鸡肉切丁,用料酒和淀粉腌制","花生米炸至金黄盛出","热锅下油,爆香干辣椒和花椒","下鸡丁快速翻炒至变色","加入酱油和白糖调味","加入花生米翻炒均匀出锅"]
}
  1. 保存文件
  2. 重新构建:npm run dev
  3. 刷新浏览器,看到新增的宫保鸡丁菜谱

学习目标:

  • 理解数据驱动视图的概念
  • React 会自动根据数据变化更新 UI
  • 无需手动操作 DOM

练习 2:新增评分组件 ⭐⭐

任务描述:
创建一个 Rating 组件,在每个菜谱中显示星级评分。

步骤 1:创建 Rating 组件

创建 src/components/Rating.js

import React from 'react';function Rating({ rating }) {const stars = [];for (let i = 1; i <= 5; i++) {if (i <= rating) {stars.push(<span key={i} style={{ color: '#FFD700', fontSize: '1.5rem' }}></span>);} else {stars.push(<span key={i} style={{ color: '#ddd', fontSize: '1.5rem' }}></span>);}}return <div style={{ margin: '10px 0' }}>{stars}</div>;
}export default Rating;

步骤 2:在 Recipe 组件中使用

修改 src/components/Recipe.js

import React from 'react';
import IngredientsList from './IngredientsList';
import Instructions from './Instructions';
import Rating from './Rating';  // 新增导入function Recipe({ name, ingredients, steps, rating }) {  // 新增 rating 参数return (<section className="recipe"><h2>{name}</h2><Rating rating={rating} />  {/* 新增评分组件 */}<IngredientsList list={ingredients} /><Instructions title="烹饪步骤" steps={steps} /></section>);
}export default Recipe;

步骤 3:更新数据文件

src/data/recipes.json 中为每道菜添加 rating 字段:

[{"name": "意大利面","rating": 5,"ingredients": [...],"steps": [...]},{"name": "炒饭","rating": 4,"ingredients": [...],"steps": [...]},{"name": "番茄炒蛋","rating": 5,"ingredients": [...],"steps": [...]}
]

步骤 4:重新构建并查看

npm run dev

刷新浏览器,每个菜谱下方会显示星级评分。

学习目标:

  • 创建新组件的完整流程
  • 在父组件中引入和使用子组件
  • 通过 props 传递数据
  • 更新数据结构以支持新功能

练习 3:添加样式优化 ⭐

任务描述:
修改 CSS 样式,让菜谱卡片更加美观。

步骤 1:修改 dist/index.html 中的样式

<style> 标签中添加或修改:

/* 为评分组件添加样式 */
.recipe .rating {display: flex;align-items: center;margin: 15px 0;
}/* 让配料项悬停时高亮 */
.ingredients li:hover {background-color: #e9ecef;padding-left: 10px;transition: all 0.3s ease;
}/* 为步骤添加更好的视觉效果 */
.instructions li {background: #f8f9fa;padding: 12px;margin: 10px 0;border-radius: 8px;border-left: 3px solid #667eea;
}.instructions li:hover {background: #e9ecef;transform: translateX(5px);transition: all 0.3s ease;
}/* 为卡片添加渐变边框效果 */
.recipe {position: relative;border: 2px solid transparent;background-clip: padding-box;
}.recipe:before {content: '';position: absolute;top: -2px;left: -2px;right: -2px;bottom: -2px;background: linear-gradient(135deg, #667eea, #764ba2);border-radius: 16px;z-index: -1;opacity: 0;transition: opacity 0.3s ease;
}.recipe:hover:before {opacity: 1;
}

步骤 2:查看效果

直接刷新浏览器(不需要重新构建,因为改的是 HTML 文件),体验:

  • 配料项悬停高亮
  • 步骤项悬停移动
  • 卡片悬停渐变边框

学习目标:

  • 理解样式与组件的关系
  • CSS 可以独立于 React 组件修改
  • 使用现代 CSS 技术增强用户体验

5.2 参考实现

完整的 Rating 组件
import React from 'react';function Rating({ rating }) {// 确保 rating 在 0-5 之间const normalizedRating = Math.max(0, Math.min(5, rating));const stars = [];for (let i = 1; i <= 5; i++) {stars.push(<span key={i} style={{ color: i <= normalizedRating ? '#FFD700' : '#ddd',fontSize: '1.5rem',marginRight: '2px'}}></span>);}return (<div className="rating" style={{ margin: '10px 0' }}>{stars}<span style={{ marginLeft: '10px', color: '#666' }}>({normalizedRating}/5)</span></div>);
}export default Rating;
完整更新后的 recipes.json
[{"name": "意大利面","rating": 5,"ingredients": [{ "name": "意大利面", "amount": 200, "measurement": "克" },{ "name": "番茄酱", "amount": 100, "measurement": "克" },{ "name": "洋葱", "amount": 1, "measurement": "个" },{ "name": "大蒜", "amount": 3, "measurement": "瓣" },{ "name": "橄榄油", "amount": 2, "measurement": "汤匙" }],"steps": ["煮沸一锅盐水","加入意大利面煮 8-10 分钟","热锅加橄榄油,爆香蒜末和洋葱丁","加入番茄酱翻炒均匀","将煮好的面条加入酱汁中拌匀","装盘即可享用"]},{"name": "炒饭","rating": 4,"ingredients": [{ "name": "米饭", "amount": 2, "measurement": "碗" },{ "name": "鸡蛋", "amount": 2, "measurement": "个" },{ "name": "胡萝卜", "amount": 50, "measurement": "克" },{ "name": "青豆", "amount": 30, "measurement": "克" },{ "name": "酱油", "amount": 1, "measurement": "汤匙" }],"steps": ["鸡蛋打散炒熟后盛出","胡萝卜切丁,与青豆一起炒熟","加入米饭翻炒","加入炒好的鸡蛋","倒入酱油调味","翻炒均匀后出锅"]},{"name": "番茄炒蛋","rating": 5,"ingredients": [{ "name": "番茄", "amount": 3, "measurement": "个" },{ "name": "鸡蛋", "amount": 4, "measurement": "个" },{ "name": "白糖", "amount": 1, "measurement": "勺" },{ "name": "盐", "amount": 1, "measurement": "勺" },{ "name": "食用油", "amount": 2, "measurement": "汤匙" }],"steps": ["番茄切块,鸡蛋打散","热锅下油,炒鸡蛋至半熟盛出","再下油,炒番茄块至出汁","加入白糖和盐调味","倒入炒好的鸡蛋","翻炒均匀后出锅"]},{"name": "宫保鸡丁","rating": 5,"ingredients": [{ "name": "鸡胸肉", "amount": 300, "measurement": "克" },{ "name": "花生米", "amount": 100, "measurement": "克" },{ "name": "干辣椒", "amount": 10, "measurement": "个" },{ "name": "花椒", "amount": 1, "measurement": "勺" },{ "name": "酱油", "amount": 2, "measurement": "汤匙" }],"steps": ["鸡肉切丁,用料酒和淀粉腌制","花生米炸至金黄盛出","热锅下油,爆香干辣椒和花椒","下鸡丁快速翻炒至变色","加入酱油和白糖调味","加入花生米翻炒均匀出锅"]}
]

第六部分:核心概念回顾

6.1 React 核心思想

组件化(Component-Based)

将 UI 拆分成独立、可复用的组件:

应用(大)↓ 拆分
Menu 组件(中)↓ 拆分
Recipe 组件(中)↓ 拆分
IngredientsList、Instructions(小)↓ 拆分
Ingredient(最小)

优势:

  • 代码复用:Ingredient 组件可以在任何地方使用
  • 职责单一:每个组件只做一件事
  • 易于维护:修改某个组件不影响其他组件
  • 团队协作:不同开发者可以并行开发不同组件
声明式编程(Declarative)

命令式(传统方式):

// 告诉计算机"怎么做"
const ul = document.createElement('ul');
data.forEach(item => {const li = document.createElement('li');li.textContent = item;ul.appendChild(li);
});
document.body.appendChild(ul);

声明式(React 方式):

// 告诉计算机"是什么"
function List({ data }) {return (<ul>{data.map(item => <li>{item}</li>)}</ul>);
}

React 自动处理 DOM 更新,你只需描述 UI 的最终状态。

单向数据流(Unidirectional Data Flow)

数据从父组件流向子组件,通过 props 传递:

Menu (recipes 数据)↓ props
Recipe (单个菜谱数据)↓ props
IngredientsList (配料数组)↓ props
Ingredient (单个配料)

优势:

  • 数据流向清晰,易于追踪
  • 便于调试,知道数据来自哪里
  • 避免数据混乱,子组件不能修改 props

6.2 工程化工具链

Webpack 的作用

Webpack 是一个模块打包器,核心概念是依赖图

入口文件 (src/index.js)↓ import Menu
Menu.js↓ import Recipe
Recipe.js↓ import IngredientsList, Instructions
IngredientsList.js↓ import Ingredient
Ingredient.js
Instructions.js↓ 打包
bundle.js (所有代码合并)

工作流程:

  1. entry 入口文件开始
  2. 分析所有 import 语句
  3. 递归构建依赖图
  4. 使用对应的 loader 处理文件(JS、CSS、图片等)
  5. 将所有模块打包成一个或多个 bundle 文件

为什么需要打包?

  • 浏览器不直接支持 ES6 模块
  • 减少 HTTP 请求(多个文件→一个文件)
  • 代码压缩优化
Babel 的作用

Babel 是一个JavaScript 编译器,负责语法转译:

JSX 转译:

// 源码
<div className="container"><h1>Hello</h1>
</div>// 转译后
React.createElement("div",{ className: "container" },React.createElement("h1", null, "Hello")
)

ES6+ 转译:

// 源码
const greeting = (name) => `Hello, ${name}`;// 转译后
var greeting = function(name) {return "Hello, " + name;
};

为什么需要转译?

  • 浏览器不认识 JSX
  • 旧浏览器不支持 ES6+ 语法
  • 让你能使用最新的 JavaScript 特性

6.3 开发流程总结

完整的构建流程:

┌─────────────────┐
│ 编写源码         │  JSX + ES6+ + 模块化
│ src/index.js    │
│ src/components/ │
└────────┬────────┘↓
┌─────────────────┐
│ Webpack 读取    │  从 entry 开始构建依赖图
└────────┬────────┘↓
┌─────────────────┐
│ Babel 转译      │  JSX → JS, ES6+ → ES5
│ (babel-loader)  │
└────────┬────────┘↓
┌─────────────────┐
│ 打包输出        │  生成 bundle.js
│ dist/assets/    │
└────────┬────────┘↓
┌─────────────────┐
│ 浏览器加载      │  执行 bundle.js
│ index.html      │
└─────────────────┘

开发工作流:

  1. 开发阶段:
    • 编写组件代码
    • 运行 npm run dev
    • 在浏览器中查看效果
    • 修改代码 → 重新构建 → 刷新浏览器
  2. 部署阶段:
    • 运行 npm run build
    • 生成优化后的生产代码
    • dist/ 目录部署到服务器

附录:完整代码清单

A. 配置文件

package.json
{"name": "recipes-app","version": "1.0.0","scripts": {"dev": "webpack --mode development","build": "webpack --mode production"},"dependencies": {"react": "^18.2.0","react-dom": "^18.2.0"},"devDependencies": {"@babel/core": "^7.23.0","@babel/preset-env": "^7.23.0","@babel/preset-react": "^7.22.0","babel-loader": "^9.1.3","webpack": "^5.89.0","webpack-cli": "^5.1.4"}
}
.babelrc
{"presets": ["@babel/preset-env", "@babel/preset-react"]
}
webpack.config.js
const path = require('path');module.exports = {entry: './src/index.js',output: {path: path.join(__dirname, 'dist', 'assets'),filename: 'bundle.js'},module: {rules: [{test: /\.jsx?$/,exclude: /node_modules/,loader: 'babel-loader'}]},devtool: 'source-map'
};
dist/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>React 菜谱应用</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Arial', sans-serif;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;padding: 20px;}#root {max-width: 1200px;margin: 0 auto;}article > header {text-align: center;margin-bottom: 40px;}article > header h1 {color: white;font-size: 3rem;text-shadow: 2px 2px 4px rgba(0,0,0,0.3);margin-bottom: 10px;}.recipes {display: grid;grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));gap: 30px;}.recipe {background: white;border-radius: 16px;padding: 30px;box-shadow: 0 10px 30px rgba(0,0,0,0.2);transition: transform 0.3s ease;}.recipe:hover {transform: translateY(-5px);}.recipe h2 {color: #667eea;font-size: 2rem;margin-bottom: 20px;border-bottom: 3px solid #667eea;padding-bottom: 10px;}.ingredients {background: #f8f9fa;border-radius: 12px;padding: 20px;margin-bottom: 25px;border-left: 5px solid #667eea;}.ingredients li {list-style: none;padding: 8px 0;color: #495057;font-size: 1.05rem;}.ingredients li:before {content: "✓ ";color: #667eea;font-weight: bold;margin-right: 8px;}.instructions {margin-top: 20px;}.instructions h3 {color: #495057;font-size: 1.3rem;margin-bottom: 15px;}.instructions ol {padding-left: 25px;}.instructions li {margin: 12px 0;line-height: 1.6;color: #6c757d;font-size: 1.05rem;}.instructions li:before {font-weight: bold;color: #667eea;}</style>
</head>
<body><div id="root"></div><script src="assets/bundle.js"></script>
</body>
</html>

B. 数据文件

src/data/recipes.json
[{"name": "意大利面","ingredients": [{ "name": "意大利面", "amount": 200, "measurement": "克" },{ "name": "番茄酱", "amount": 100, "measurement": "克" },{ "name": "洋葱", "amount": 1, "measurement": "个" },{ "name": "大蒜", "amount": 3, "measurement": "瓣" },{ "name": "橄榄油", "amount": 2, "measurement": "汤匙" }],"steps": ["煮沸一锅盐水","加入意大利面煮 8-10 分钟","热锅加橄榄油,爆香蒜末和洋葱丁","加入番茄酱翻炒均匀","将煮好的面条加入酱汁中拌匀","装盘即可享用"]},{"name": "炒饭","ingredients": [{ "name": "米饭", "amount": 2, "measurement": "碗" },{ "name": "鸡蛋", "amount": 2, "measurement": "个" },{ "name": "胡萝卜", "amount": 50, "measurement": "克" },{ "name": "青豆", "amount": 30, "measurement": "克" },{ "name": "酱油", "amount": 1, "measurement": "汤匙" }],"steps": ["鸡蛋打散炒熟后盛出","胡萝卜切丁,与青豆一起炒熟","加入米饭翻炒","加入炒好的鸡蛋","倒入酱油调味","翻炒均匀后出锅"]},{"name": "番茄炒蛋","ingredients": [{ "name": "番茄", "amount": 3, "measurement": "个" },{ "name": "鸡蛋", "amount": 4, "measurement": "个" },{ "name": "白糖", "amount": 1, "measurement": "勺" },{ "name": "盐", "amount": 1, "measurement": "勺" },{ "name": "食用油", "amount": 2, "measurement": "汤匙" }],"steps": ["番茄切块,鸡蛋打散","热锅下油,炒鸡蛋至半熟盛出","再下油,炒番茄块至出汁","加入白糖和盐调味","倒入炒好的鸡蛋","翻炒均匀后出锅"]}
]

C. 组件文件

src/components/Ingredient.js
import React from 'react';function Ingredient({ amount, measurement, name }) {return (<li>{amount} {measurement} {name}</li>);
}export default Ingredient;
src/components/IngredientsList.js
import React from 'react';
import Ingredient from './Ingredient';function IngredientsList({ list }) {return (<ul className="ingredients">{list.map((ingredient, i) => (<Ingredient key={i} {...ingredient} />))}</ul>);
}export default IngredientsList;
src/components/Instructions.js
import React from 'react';function Instructions({ title, steps }) {return (<section className="instructions"><h3>{title}</h3><ol>{steps.map((step, i) => (<li key={i}>{step}</li>))}</ol></section>);
}export default Instructions;
src/components/Recipe.js
import React from 'react';
import IngredientsList from './IngredientsList';
import Instructions from './Instructions';function Recipe({ name, ingredients, steps }) {return (<section className="recipe"><h2>{name}</h2><IngredientsList list={ingredients} /><Instructions title="烹饪步骤" steps={steps} /></section>);
}export default Recipe;
src/components/Menu.js
import React from 'react';
import Recipe from './Recipe';function Menu({ recipes }) {return (<article><header><h1>美味菜谱</h1></header><div className="recipes">{recipes.map((recipe, i) => (<Recipe key={i} {...recipe} />))}</div></article>);
}export default Menu;

D. 入口文件

src/index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import Menu from './components/Menu';
import data from './data/recipes.json';const root = createRoot(document.getElementById('root'));
root.render(<Menu recipes={data} />);

总结

通过本教程,你已经学会了:

✅ React 核心概念

  • 组件化开发思维
  • JSX 语法的使用
  • Props 数据传递
  • 声明式编程范式

✅ 工程化工具配置

  • Webpack 打包配置
  • Babel 转译配置
  • 开发与生产构建
  • Source Map 调试

✅ 实战开发能力

  • 从需求到组件设计
  • 自底向上构建组件树
  • 数据驱动视图更新
  • 组件的创建、组合、复用

✅ 完整开发流程

需求分析 → 数据设计 → 组件拆分 → 编写代码 → 构建打包 → 浏览器运行

快速命令参考

# 创建项目
mkdir recipes-app && cd recipes-app# 安装依赖
npm init -y
npm install react react-dom
npm install -D webpack webpack-cli babel-loader @babel/core @babel/preset-env @babel/preset-react# 开发构建
npm run dev# 生产构建
npm run build# 查看效果
# 打开 dist/index.html

项目结构总览

recipes-app/
├── package.json           # 项目配置和依赖
├── .babelrc              # Babel 转译配置
├── webpack.config.js     # Webpack 打包配置
├── dist/                 # 构建输出目录
│   ├── index.html        # 应用入口页面
│   └── assets/
│       ├── bundle.js            # 打包后的代码
│       ├── bundle.js.map        # Source Map
│       └── bundle.js.LICENSE    # 第三方库许可
└── src/                  # 源代码目录├── index.js          # 应用入口文件├── components/       # 组件目录│   ├── Menu.js              # 菜单组件(根组件)│   ├── Recipe.js            # 菜谱组件│   ├── IngredientsList.js   # 配料列表组件│   ├── Ingredient.js        # 单个配料组件│   └── Instructions.js      # 步骤说明组件└── data/            # 数据目录└── recipes.json         # 菜谱数据

恭喜你完成了第一个 React 应用!现在你可以:

  1. 扩展功能:添加搜索、筛选、收藏等功能
  2. 学习状态管理:使用 useStateuseEffect Hook
  3. 添加路由:使用 React Router 实现多页面
  4. 连接后端:通过 API 获取数据
  5. 学习样式方案:CSS Modules、Styled Components、Tailwind CSS

React 的学习之旅才刚刚开始,继续探索吧!🚀

http://www.dtcms.com/a/453117.html

相关文章:

  • 网站备案和域名备案网页源码app
  • Tomcat本地部署SpringBoot项目
  • 大模型开发 - 04 QuickStart_DeepSeek 模型调用流程源码解析:从 Prompt 到远程请求
  • 怎么把在微企点做响应式网站深圳专业网站建
  • 认识三极管
  • gRPC从0到1系列【23】
  • Element Plus 完整教程:从背景到实践
  • Qt编写上下界面切换效果/前进到下一个界面/后退到上一个页面/零件工艺及管理设计系统
  • 第3章 多线程服务器的适用场合与常用编程模型
  • 网站开发什么课程佛山建站模板制作
  • Lua语法(2)
  • npm、npx、pnpm 深度解析:从原理到实战的全方位指南
  • Qt Qml Drag and Drop-鼠标拖动添加组件
  • 神经网络之为什么回归任务的输出是高斯分布的均值
  • 《深入理解 Django 中间件:请求-响应生命周期与执行顺序全解析》
  • HC32项目搭建
  • 台式真空共晶炉口碑企业
  • 网站开发宣传标语网站建设基本情况
  • [效率]学习哔哩哔哩视频的的笔记|对于书签的想法思考
  • 网站一级页面标题怎么做wordpress js库
  • Python 数字类型与类型转换
  • Python int()函数
  • 【Qt】绘图
  • Java 集合框架全解析:从数据结构到源码实战
  • 北京商地网站建设公司photoshop设计一个精美的网站主页
  • 【MYSQL】统计用户旅行距离的SQL解决方案:排序规则与稳定性全解析
  • 基于单片机的罐体压力控制器设计与实现
  • C# datagridview读取XML数据和保存到XML的例子
  • OPENPPP2 静态隧道链路迁移平滑(UDP/IP)
  • 使用Unity引擎开发Rokid主机应用的模型交互操作