在这篇文章中,我们将深入探讨如何使用 React 构建一个经典的项目——“极客购物车”。虽然这看起来是一个基础的教程项目,但作为经验丰富的开发者,我们知道,即使在 2026 年,掌握核心的状态管理和组件化逻辑依然是构建复杂 AI 原生应用的基石。我们将这个项目视为一个打磨基础的最佳实践,不仅关注功能实现,更融入了现代工程化的理念。
目录
为什么我们要在 2026 年重写这个经典项目?
你可能会问,购物车教程不是满大街都是吗?确实如此。但在我们最近的团队复盘中,我们发现很多初级开发者在追求新技术(如 SSR 微前端或复杂的 AI Agent)时,往往忽略了 React 最本质的 单向数据流 和 状态复用 逻辑。通过这个项目,我们希望展示如何像编写企业级代码一样编写一个简单的应用,包括关注点分离、可维护的 CSS 架构以及清晰的组件职责划分。
让我们来看看最终项目的样子。这不仅仅是一个 UI,它包含了完整的增删改查(CRUD)闭环逻辑。
!<a href="https://media.geeksforgeeks.org/wp-content/uploads/20240730125642/IMAGE1.jpg">IMAGE1
技术栈与 2026 年的开发环境:
在 2026 年,我们不再使用 create-react-app,因为它已不再维护且效率低下。在这个项目中,我们将采用更现代的工具链:
- React 19+: 利用最新的 Compiler 特性(虽然我们这里主要编写函数组件)。
- Vite: 下一代前端构建工具,提供极速的冷启动和热更新(HMR)。
- CSS Modules / Scoped CSS: 为了避免全局样式污染,我们将演示如何优雅地组织样式。
项目结构设计:工程化的起点
在我们开始编写代码之前,让我们思考一下目录结构。一个好的结构能让代码具有自解释性。我们将按照功能模块划分文件,而不是简单的类型划分。
gfg-shopping-cart/
├── public/
├── src/
│ ├── components/
│ │ ├── SearchComponent.jsx
│ │ ├── ShowCourseComponent.jsx
│ │ └── UserCartComponent.jsx
│ ├── App.jsx # 主容器与状态管理核心
│ ├── App.css # 全局样式
│ └── main.jsx # 入口文件
├── package.json
└── vite.config.js
核心实现逻辑:深入状态管理
在这个应用中,我们选择将所有状态(课程列表、购物车内容、搜索关键词)提升到 App.jsx 中。这种“单一数据源”的策略让我们能够更容易地实现诸如“实时计算总金额”和“搜索过滤”等跨组件功能。
#### 1. App.jsx: 应用的大脑
这是整个应用的逻辑核心。我们不仅定义了数据,还定义了操作数据的“动作”(Actions)。注意看我们如何处理 addCourseToCart 函数,它包含了防止重复添加的业务逻辑,这是我们在生产环境中经常遇到的边界情况。
// src/App.jsx
import React, { useState } from ‘react‘;
import ‘./App.css‘;
import SearchComponent from ‘./components/SearchComponent‘;
import ShowCourseComponent from ‘./components/ShowCourseComponent‘;
import UserCartComponent from ‘./components/UserCartComponent‘;
function App() {
// 定义状态:课程列表、购物车、搜索词
const [courses, setCourses] = useState([
{ id: 1, name: ‘React JS 基础‘, price: 999, image: ‘...‘ },
{ id: 2, name: ‘Node.js 高级实战‘, price: 1299, image: ‘...‘ },
{ id: 3, name: ‘Vue3 全栈开发‘, price: 899, image: ‘...‘ },
// ... 更多课程数据
]);
const [cartCourses, setCartCourses] = useState([]);
const [searchCourse, setSearchCourse] = useState(‘‘);
// 添加课程到购物车的逻辑
const addCourseToCart = (course) => {
// 检查课程是否已在购物车中
const isAlreadyAdded = cartCourses.find(
(item) => item.id === course.id
);
if (isAlreadyAdded) {
// 如果已存在,我们可以选择提示用户或增加数量
// 这里为了简单,我们不做重复添加
return;
}
// 更新购物车状态
setCartCourses([...cartCourses, course]);
};
// 移除课程的逻辑
const removeCourse = (course) => {
const updatedCart = cartCourses.filter(
(item) => item.id !== course.id
);
setCartCourses(updatedCart);
};
// 计算总价格
const calculateTotalAmount = () => {
return cartCourses.reduce((total, course) => total + course.price, 0);
};
// 购买按钮点击处理
const handlePurchase = () => {
if (cartCourses.length === 0) {
alert(‘购物车是空的,无法结账!‘);
return;
}
alert(`购买成功!总金额:${calculateTotalAmount()}`);
setCartCourses([]); // 清空购物车
};
return (
极客购物车
{/* 搜索组件 */}
{/* 商品展示组件 */}
course.name.toLowerCase().includes(searchCourse.toLowerCase())
)}
addCourseToCart={addCourseToCart}
/>
{/* 购物车组件 */}
);
}
export default App;
#### 2. 展示组件:解耦 UI 与 逻辑
在 2026 年,我们强调组件的“哑组件”属性。它们只负责渲染 UI,通过 Props 接收数据和回调函数。
ShowCourseComponent.jsx:
// src/components/ShowCourseComponent.jsx
import React from ‘react‘;
function ShowCourseComponent({
courses, // 原始数据(未使用但保留作为示例)
filteredCourses, // 过滤后的数据
addCourseToCart // 回调函数
}) {
return (
{/* 这里我们可以优化为按列显示 */}
{filteredCourses.map((course) => (
{course.name}
Rs {course.price}
))}
{filteredCourses.length === 0 && (
没有找到匹配的课程。
)}
);
}
export default ShowCourseComponent;
UserCartComponent.jsx:
// src/components/UserCartComponent.jsx
import React from ‘react‘;
function UserCartComponent({
cartCourses,
removeCourse,
totalAmount,
handlePurchase
}) {
return (
你的购物车
{cartCourses.length === 0 ? (
购物车是空的。
) : (
{cartCourses.map((course) => (
{course.name}
Rs {course.price}
))}
总金额: Rs {totalAmount}
)}
);
}
export default UserCartComponent;
#### 3. 现代化样式:CSS 变量与 Flexbox
虽然我们这里在一个 CSS 文件中编写样式,但在实际的大型项目中,我们建议使用 CSS-in-JS (如 styled-components) 或 Tailwind CSS。为了保持本教程的纯粹性,我们使用原生 CSS,但采用了现代化的属性。
/* App.css - 补充完整的样式定义 */
/* 布局优化 */
.App-main {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.product-list {
display: grid;
grid-template-columns: 2fr 1fr; /* 左侧商品,右侧购物车 */
gap: 30px;
margin-top: 20px;
}
/* 响应式设计:在移动端堆叠显示 */
@media (max-width: 768px) {
.product-list {
grid-template-columns: 1fr;
}
}
/* 商品卡片样式 */
.course-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}
.product-card {
background-color: rgb(255, 245, 245);
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
transition: transform 0.2s;
}
.product-card:hover {
transform: translateY(-5px);
}
.add-to-cart-btn {
background-color: #6cc24a;
color: white;
border: none;
padding: 10px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.add-to-cart-btn:hover {
background-color: #5aae38;
}
/* 购物车样式 */
.cart-container {
background-color: #fff;
border: 1px solid #eee;
border-radius: 8px;
padding: 20px;
height: fit-content;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #f0f0f0;
}
.checkout-btn {
width: 100%;
background-color: #333;
color: white;
padding: 12px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
2026 开发视角:技术债务与未来优化
作为一名经验丰富的开发者,我必须指出,上述代码虽然逻辑清晰,但在现代大型生产环境中还存在局限性。让我们来探讨一下如果我们要把这个应用推向市场,还需要考虑什么。
1. 状态管理的演进:从 useState 到 Zustand/Redux
在这个简单的例子中,使用 useState 进行“Prop Drilling”(属性透传)是可以接受的。但想象一下,如果我们有 20 个组件都需要访问购物车数据?
在 2026 年,我们可能会选择 Zustand 或 Jotai 这样的轻量级状态管理库。它们摒弃了 Redux 的样板代码,提供了更直观的 API。例如,我们可以创建一个 useCartStore,任何组件都可以直接读取和修改购物车,而不需要层层传递 Props。
2. 性能优化的深水区:useMemo 与 Virtualization
你有没有想过,如果我们的课程列表有 10,000 条数据,单纯的 map 渲染会导致页面卡顿?
我们通常会引入 React Virtual 或 TanStack Virtual 这样的库,只渲染可视区域内的 DOM 节点。此外,对于 INLINECODEdd1071d3 和 INLINECODE96fdb5d4 的计算,务必使用 useMemo 进行缓存,避免在每次组件重渲染时都重新进行昂贵的计算或过滤操作。
3. AI 辅助开发实战:我们是如何写的
在编写这段代码时,我们利用了 AI 辅助工具(如 Cursor 或 GitHub Copilot)。
- 生成组件骨架: 我们输入了
// Create a functional component named UserCartComponent that accepts cartCourses and removeCourse,AI 瞬间生成了组件结构。 - CSS 调优: 我们让 AI “把这段 CSS 改成 Flexbox 布局并添加 hover 效果”,它迅速给出了现代化的样式代码。
这不仅是代码生成,更是“结对编程”的体现。AI 帮助我们处理了繁琐的语法工作,让我们专注于业务逻辑——即“如何让购物车体验更好”。
总结
通过构建这个“极客购物车”,我们不仅复习了 React 的核心概念,更重要的是,我们以 2026 年的视角重新审视了基础架构的重要性。无论是处理边缘情况(如重复添加商品),还是考虑到未来的扩展性(CSS 模块化、状态管理),都是我们通往高级开发者的必经之路。
我们鼓励你在这个基础上进行扩展:尝试接入一个真实的支付 API(如 Stripe),或者使用 LocalStorage 持久化购物车数据,甚至使用 React Query 来管理服务器状态。在这个过程中,你会遇到各种 Bug,但这正是学习的最佳时机。
在下一篇文章中,我们将探讨如何将此应用部署到 Vercel 或 Netlify 的 Edge Network 上,实现全球秒开。祝你编码愉快!