项目开搞(一):带你边笑边学用 React 全家桶撸个 GitHub Repos 页面!

76 阅读3分钟

别人撸项目像在打怪升级,小白撸项目像在被怪打晕。
别怕,这篇文章手把手带你写一个“看起来很厉害”的 React 项目,妈妈再也不担心我只会写 demo 了!


本文适合谁看?

  • 想学会项目级开发但只会 useState 的 React 小白
  • 想了解怎么封装 API、搞全局状态、设计路由的萌新
  • 想用代码唬住朋友、骗面试官你很牛的同学(误)

项目背景设定

你准备做一个小项目,输入某个 GitHub 用户名,就能看到他所有公开仓库,展示内容如下:

  • 仓库名
  • 描述
  • Star 数

好像不难对吧?

但我们不是写个小 demo 玩玩,而是要像个正经程序员那样封装架构、分层模块、优化性能,做出一个“项目级别”的 React 应用!


技术栈:全家桶套餐

技术用处备注
ReactUI 框架毕竟你都搜到这了
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,而是如何写「好维护的项目」。

如果你看到这已经跃跃欲试了——

点个赞收藏支持下!
留言说下你想扩展啥功能!
想看下一篇?评论区催更!