别人撸项目像在打怪升级,小白撸项目像在被怪打晕。
别怕,这篇文章手把手带你写一个“看起来很厉害”的 React 项目,妈妈再也不担心我只会写 demo 了!
本文适合谁看?
- 想学会项目级开发但只会
useState的 React 小白 - 想了解怎么封装 API、搞全局状态、设计路由的萌新
- 想用代码唬住朋友、骗面试官你很牛的同学(误)
项目背景设定
你准备做一个小项目,输入某个 GitHub 用户名,就能看到他所有公开仓库,展示内容如下:
- 仓库名
- 描述
- Star 数
好像不难对吧?
但我们不是写个小 demo 玩玩,而是要像个正经程序员那样封装架构、分层模块、优化性能,做出一个“项目级别”的 React 应用!
技术栈:全家桶套餐
| 技术 | 用处 | 备注 |
|---|---|---|
| React | UI 框架 | 毕竟你都搜到这了 |
| react-router-dom | 页面切换 | SPA 路由方案 |
| axios | 数据请求 | 比 fetch 更好用 |
| Context + Reducer | 全局状态管理 | 轻量版 Redux |
| Suspense + lazy | 页面懒加载 | 性能优化神器 |
项目结构(越规范越帅)
src/
├── api/ // 封装所有接口
├── components/ // 通用组件(如 Loading)
├── context/ // 全局状态管理
├── pages/ // 页面组件
├── App.jsx // 路由配置
└── main.jsx // 应用入口
第一步:接口怎么封装?
新手写法:直接在组件里用 axios 乱请求
老手写法:统一管理所有接口
// api/repos.js
import axios from 'axios';
const BASE_URL = 'https://api.github.com/';
export const getRepos = (username) => {
return axios.get(`${BASE_URL}users/${username}/repos`);
};
export const getRepoDetail = (username, repoName) => {
return axios.get(`${BASE_URL}repos/${username}/${repoName}`);
};
✅ 封装后好处:
- 请求逻辑和 UI 彻底分离
- 换接口、加拦截器、加 token 都超级方便
- 看起来就很专业
第二步:状态怎么全局管理?
页面要展示仓库数据,那 repos 要放哪儿?
我们用 createContext + useReducer 来搞一个轻量版的 Redux!
// context/GlobalContext.jsx
import { createContext, useReducer, useContext } from "react";
const initialState = {
repos: [],
loading: false,
error: null
};
function reducer(state, action) {
switch (action.type) {
case 'SET_REPOS':
return { ...state, repos: action.payload };
case 'SET_LOADING':
return { ...state, loading: action.payload };
case 'SET_ERROR':
return { ...state, error: action.payload };
default:
return state;
}
}
export const GlobalContext = createContext();
export const GlobalProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<GlobalContext.Provider value={{ state, dispatch }}>
{children}
</GlobalContext.Provider>
);
};
export const useGlobalContext = () => useContext(GlobalContext);
简单说:你以后在任何页面里都能直接用
useGlobalContext()来获取或修改全局状态。再也不用 props 一层层传了!
第三步:页面路由怎么写?
// App.jsx
import { lazy, Suspense } from 'react'
import { Routes, Route, Navigate } from 'react-router-dom'
import Loading from './components/Loading'
const RepoList = lazy(() => import('./pages/RepoList'))
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/users/:id/repos" element={<RepoList />} />
<Route path="*" element={<Navigate to="/users/hanhanyiduo/repos" />} />
</Routes>
</Suspense>
)
}
✅ 知识点汇总:
lazy懒加载页面,提高性能Suspense包裹路由,加载时展示 Loading:id是动态路由,代表 GitHub 用户名Navigate是默认跳转
总结一下你的收获!
我们只是撸了项目的第一步,就已经学到了:
| 技术点 | 思维提升 |
|---|---|
| axios 接口封装 | 模块职责分明,不写死请求 |
| context + reducer | 全局状态更可控 |
| 懒加载 + Suspense | 性能也得顾 |
| 动态路由 + useParams | 页面可复用 |
| 组件拆分思路 | 页面 vs 展示 vs 工具函数 |
接下来能做什么?
- ✅ 给仓库加详情页(点击仓库查看详情)
- ✅ 搜索框过滤 repo 名
- ✅ 增加分页或 loading skeleton
- ✅ 上线部署(Netlify、Vercel)
- ✅ 配合 UI 库美化界面(Tailwind、AntD)
彩蛋预告:项目第二篇要写啥?
下一篇我们来搞:
如何用一个 reducer 管理多个状态页面?
如何封装通用 Loading、Error 组件?
如何做仓库详情页?点击跳转!
最后:小白写项目,其实不难!
写项目不是背 API,而是学思维。
你学的不只是 React,而是如何写「好维护的项目」。
如果你看到这已经跃跃欲试了——
点个赞收藏支持下!
留言说下你想扩展啥功能!
想看下一篇?评论区催更!