# react-antd-h5
**Repository Path**: 674074365/react-antd-h5
## Basic Information
- **Project Name**: react-antd-h5
- **Description**: react+antd+vite移动端h5项目
- **Primary Language**: Unknown
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 30
- **Forks**: 8
- **Created**: 2023-11-28
- **Last Updated**: 2026-05-24
## Categories & Tags
**Categories**: Uncategorized
**Tags**: React, vite, antd-mobile
## README
# React Antd Mobile H5
基于 **React 19 + Vite 8 (SWC) + Ant Design Mobile 5 + Redux** 的移动端 H5 电商模板项目,采用纯 JavaScript (JSX) 开发,集成路由缓存、全局自动导入、模块化 Redux、国际化、常量管理、请求封装等企业级开发方案。
## 特性
- **React 19** + **Vite 8 (SWC)** — 极速开发体验,闪电般的 HMR
- **Ant Design Mobile 5** — 高质量移动端 UI 组件库
- **路由缓存** — 基于 `react-activation` + 自定义 `useNavigate` 实现页面级 KeepAlive 缓存
- **全局自动导入** — `unplugin-auto-import` 实现 React Router / i18next / 自定义 Hooks 零导入使用
- **模块化 Redux** — 约定式目录结构,`import.meta.glob` 自动加载,支持 Redux DevTools
- **常量管理** — 模块化常量定义,`import.meta.glob` 自动加载,`$globalConstant` 统一访问
- **请求封装** — 基于 Axios 的统一请求层,支持 GET / POST / PUT / DELETE / JSONP / FormData
- **Mock 数据** — 内置 Mock 模式,支持全局 link/mock 切换,覆盖全部接口的本地模拟数据
- **国际化** — `i18next` + `react-i18next`,支持远程接口动态覆盖本地语言包
- **事件总线** — 基于 `mitt` 的全局事件通信,`$globalEventBus` 自动导入
- **Vite 插件模块化** — 自定义插件按文件自动加载(copy / zip / legacy / progress)
- **ESLint 9 + Prettier** — Flat Config 方案,0 errors 0 warnings
- **移动端适配** — `postcss-pxtorem` + `amfe-flexible` 自动 rem 转换
- **Less + CSS Modules** — 局部样式隔离 + `:global` 覆盖 UI 库样式
## 技术栈
| 类别 | 技术 | 版本 |
|------|------|------|
| 框架 | React | ^19.2.6 |
| 构建工具 | Vite (SWC) | ^8.0.11 |
| UI 组件库 | Ant Design Mobile | ^5.42.3 |
| 状态管理 | Redux + react-redux + redux-thunk + redux-promise | ^5.0.1 |
| 路由 | react-router-dom (HashRouter) | ^7.15.0 |
| 路由缓存 | react-activation | ^0.13.4 |
| 不可变状态 | immer | ^11.1.8 |
| 请求 | axios | ^1.16.0 |
| 国际化 | i18next + react-i18next | ^26.0.10 / ^17.0.7 |
| 事件总线 | mitt | ^3.0.1 |
| 样式 | Less + postcss-pxtorem | ^4.6.4 / ^6.1.0 |
| 代码规范 | ESLint 9 (flat config) + Prettier | ^9.39.4 / ^3.8.3 |
| 自动导入 | unplugin-auto-import | ^21.0.0 |
## 快速开始
### 环境要求
- Node.js >= 18.0.0
- npm >= 9.0.0
### 安装依赖
```bash
npm install
```
### 开发
```bash
npm run dev
```
启动后访问 `http://localhost:3301`
### 构建
```bash
# 测试环境构建
npm run build:dev
# 生产环境构建(构建完成后自动生成 dist.zip)
npm run build:pro
```
### 代码检查与格式化
```bash
# ESLint 检查(0 errors 0 warnings)
npm run lint
# Prettier 格式化
npm run format
# Prettier 检查
npm run format:check
```
### 文档站点
```bash
# 启动文档开发服务器
npm run docs:dev
# 构建文档
npm run docs:build
```
## 项目预览
| 登录 | 首页 | 分类 | 购物车 | 我的 |
|------|------|------|--------|------|
|  |  |  |  |  |
## 目录结构
```
react-antd-h5/
├── src/
│ ├── api/ # API 接口定义(预留)
│ ├── assets/ # 静态资源(图片、SVG)
│ ├── components/ # 公共组件
│ │ ├── footer-nav/ # 底部导航栏
│ │ ├── header-nav/ # 顶部导航栏
│ │ ├── refresh-scroll/ # 下拉刷新组件
│ │ └── wc-swiper/ # 轮播组件
│ ├── constant/ # 常量定义(模块化,按文件自动加载)
│ │ ├── eventBus.js # 事件总线常量
│ │ └── resultCode.js # 接口响应码常量
│ ├── hooks/ # 全局自动导入 Hooks
│ │ ├── useCommon.js # 公共函数(初始化数据加载、i18n 远程覆盖)
│ │ ├── useConstant.js # $globalConstant - 常量模块自动加载
│ │ ├── useCustomNavigate.js # 自定义 useNavigate(替代原生,支持缓存刷新)
│ │ ├── useEventBus.js # $globalEventBus - mitt 事件总线
│ │ ├── useFetch.js # $globalRequest / $globalRequestUrl - 请求封装
│ │ ├── useInitialize.jsx # $globalReady - 应用入口组件
│ │ ├── useKeepRouter.jsx # $globalLazy / $globalKeepRouter - 路由缓存
│ │ ├── useLoadRouter.jsx # $globalRouter - 路由自动加载
│ │ ├── usePrompt.jsx # $globalGuard - 路由守卫
│ │ ├── useRedux.js # $globalRedux / $globalReduxAction - Redux Store
│ │ └── useService.js # $globalServicer - 请求模块自动加载
│ ├── locales/ # 国际化语言包
│ │ ├── index.js # i18next 配置入口
│ │ ├── ZH-CN/ # 中文语言包
│ │ │ └── common.json
│ │ └── US-EN/ # 英文语言包
│ │ └── common.json
│ ├── redux/ # Redux 状态管理(模块化)
│ │ ├── common/ # 公共模块
│ │ │ ├── reduxAction.js
│ │ │ ├── reduxReducer.js
│ │ │ └── reduxType.js
│ │ └── login/ # 登录模块
│ │ ├── reduxAction.js
│ │ ├── reduxReducer.js
│ │ └── reduxType.js
│ ├── router/ # 路由配置(按页面拆分,自动加载)
│ │ ├── common.jsx # 公共路由(登录、404)
│ │ ├── home.jsx # 首页路由
│ │ ├── classify.jsx # 分类路由
│ │ ├── shop.jsx # 购物车路由
│ │ ├── detail.jsx # 详情路由
│ │ └── my.jsx # 我的路由
│ ├── service/ # 请求模块(模块化,按文件自动加载)
│ │ ├── common.js # 公共接口(购物车数量、商品详情、语言包)
│ │ ├── home.js # 首页接口(轮播图、精品、推荐)
│ │ ├── Classify.js # 分类接口
│ │ ├── ShopCard.js # 购物车接口
│ │ └── My.js # 个人中心接口
│ ├── styles/ # 全局样式
│ │ └── common.less
│ ├── views/ # 页面组件
│ │ ├── 404.jsx
│ │ ├── login/
│ │ ├── home/
│ │ ├── classify/
│ │ ├── detail/
│ │ ├── shop/
│ │ ├── my/
│ │ └── components/ # 页面级私有组件
│ └── main.jsx # 应用入口
├── vite-config/ # Vite 配置(模块化)
│ ├── index.js # 基础配置 + 插件自动加载
│ └── plugins/ # Vite 插件(按文件自动加载)
│ ├── unplugin-auto-import.js # 全局自动导入配置
│ ├── vite-plugin-copy.js # 构建产物复制
│ ├── vite-plugin-legacy.js # 低版本浏览器兼容
│ ├── vite-plugin-progress.js # 构建进度条
│ └── vite-plugin-zip.js # 构建产物打包
├── vite-env/ # 环境变量配置
│ ├── .env # 本地环境
│ ├── .env.dev # 测试环境
│ └── .env.prod # 生产环境
├── mock/ # Mock 数据(按模块拆分,URL 自动匹配)
│ ├── index.js # 入口聚合 + URL 匹配
│ ├── auth.js # 登录接口
│ ├── common.js # 公共接口(购物车数量、语言包、路由)
│ ├── home.js # 首页接口(轮播图、精品、推荐)
│ ├── classify.js # 分类接口
│ ├── shop.js # 购物车接口
│ ├── address.js # 地址接口
│ └── goods.js # 商品详情接口
├── docs/ # VitePress 文档站点
├── eslint.config.js # ESLint 9 flat config
├── .prettierrc # Prettier 配置
├── vite.config.js # Vite 入口配置
├── index.html # HTML 模板
└── package.json
```
## 核心功能
### 路由缓存
基于 `react-activation` 实现页面级 KeepAlive 缓存,通过路由 `meta` 配置控制缓存策略,支持两种缓存模式:
| 缓存模式 | 字段 | 说明 | 适用场景 |
|----------|------|------|----------|
| 常驻缓存 | `isAlwaysKeepAlive: true` | 页面始终保持在缓存中,永不销毁 | Tab 主页面(首页、分类、购物车、我的) |
| 按需缓存 | `isKeepAlive: true` | 需要缓存但可能被淘汰的页面 | 详情页等非 Tab 页面 |
```jsx
// src/router/home.jsx — Tab 主页面使用 isAlwaysKeepAlive
import React from "react";
const routers = [
{
path: "/home",
element: $globalLazy(React.lazy(() => import("@/views/home/index"))),
meta: {
title: "首页",
isAlwaysKeepAlive: true, // 常驻缓存,Tab 切换时保持状态
},
},
];
export default $globalKeepRouter(routers);
```
```jsx
// src/router/detail.jsx — 非Tab 页面使用 isKeepAlive
const routers = [
{
path: "/detail",
element: $globalLazy(React.lazy(() => import("@/views/detail/index"))),
meta: {
title: "详情",
isKeepAlive: true, // 按需缓存
},
},
];
```
```jsx
// 页面中使用自定义 navigate(自动处理缓存刷新)
const navigate = useNavigate();
navigate("/detail"); // 跳转到详情页
```
> 跳转缓存页面时自动执行 `refreshScope`,返回时保持之前的状态。`$globalKeepRouter` 和自定义 `useNavigate` 内部同时识别 `isKeepAlive` 和 `isAlwaysKeepAlive` 两种配置。
### 全局自动导入
通过 `unplugin-auto-import` 配置,以下 API 无需手动 import 即可直接使用:
| 全局变量 | 来源 | 说明 |
|----------|------|------|
| `$globalReady` | useInitialize | 应用入口组件 |
| `$globalRouter` | useLoadRouter | 路由自动加载 |
| `$globalLazy` | useKeepRouter | 路由懒加载包装 |
| `$globalKeepRouter` | useKeepRouter | 路由缓存配置 |
| `$globalGuard` | usePrompt | 路由守卫 |
| `$globalServicer` | useService | 请求模块自动加载 |
| `$globalRequestUrl` | useFetch | 请求 URL 拼接 |
| `$globalRequest` | useFetch | 统一请求方法 |
| `$globalSetMockMode` | useFetch | 设置 Mock 模式(link/mock 切换) |
| `$globalIsMockMode` | useFetch | 获取当前 Mock 模式状态 |
| `$globalRedux` | useRedux | Redux Store 实例 |
| `$globalReduxAction` | useRedux | Redux Action 加载 |
| `$globalEventBus` | useEventBus | mitt 事件总线 |
| `$globalConstant` | useConstant | 常量模块加载 |
| `useNavigate` | useCustomNavigate | 自定义导航(缓存感知) |
| `useLocation` | react-router-dom | 路由位置 |
| `useRoutes` | react-router-dom | 路由匹配 |
| `Navigate` | react-router-dom | 声明式导航 |
| `useTranslation` | react-i18next | 国际化 Hook |
### 常量管理
常量采用模块化定义,通过 `$globalConstant` 统一访问,与 Redux / Service 使用相同的自动加载模式:
```js
// src/constant/resultCode.js
const SUCCESS_CODE = 20000;
export { SUCCESS_CODE };
```
```js
// src/constant/eventBus.js
const GET_HOME_LIST = "GET_HOME_LIST";
const SET_HOME_LIST = "SET_HOME_LIST";
export { GET_HOME_LIST, SET_HOME_LIST };
```
```jsx
// 页面中使用
const { SUCCESS_CODE } = $globalConstant("resultCode");
const { GET_HOME_LIST } = $globalConstant("eventBus");
if (res?.code === SUCCESS_CODE) {
// 处理成功逻辑
}
```
> 新增常量文件只需在 `src/constant/` 下创建 `.js` 文件并导出即可,无需手动注册。
### 模块化 Redux
约定每个模块包含三个文件,通过 `import.meta.glob` 自动加载:
```
src/redux/{模块名}/
├── reduxType.js # Action 类型常量
├── reduxAction.js # Action 创建函数
└── reduxReducer.js # Reducer 函数(默认导出)
```
```jsx
// 页面中使用
const { setShopNum } = $globalReduxAction("common");
const Home = (props) => {
const { setShopNum } = props;
return ...
;
};
const mapStateToProps = (state) => state.common;
const mapDispatchToProps = { setShopNum };
export default connect(mapStateToProps, mapDispatchToProps)(Home);
```
### 请求封装
基于 Axios 的统一请求层,支持多种请求方式和数据格式:
```js
// 拼接请求 URL
const url = $globalRequestUrl({ url: "/getGoodsList" });
// 发起请求
const [data, err] = await $globalRequest(url, params, { method: "GET" });
```
Service 层统一使用 `[data, err]` 元组模式返回,配合常量判断响应状态:
```jsx
const { getGoodsNum } = $globalServicer("common");
const [res] = await getGoodsNum();
const { SUCCESS_CODE } = $globalConstant("resultCode");
if (res?.code === SUCCESS_CODE) {
$globalRedux.dispatch({ type: "SET_SHOP_NUM", shopNum: res?.data || 0 });
}
```
支持的请求方法:`GET`、`POST`、`POST_JSON`、`POST_FORMDATA`、`POST_FORM_URLENCODED`、`PUT`、`PUT_FORDATA`、`DELETE`、`JSONP`
### Mock 数据模式
项目内置了全局 Mock 模式,无需后端即可进行前端开发调试。通过登录页面右上角的下拉组件或代码 API 进行 link/mock 切换:
**切换方式:**
| 方式 | 说明 |
|------|------|
| 登录页下拉 | 页面右上角点击 link/mock 按钮,选择模式即可切换 |
| 代码 API | `$globalSetMockMode(true)` 开启 / `$globalSetMockMode(false)` 关闭 |
**工作原理:**
```js
// 开启 Mock 模式后,$globalRequest 内部拦截所有请求
if (window.__MOCK_ENABLED__) {
const mockConfig = matchMock(url);
if (mockConfig) {
return Promise.resolve({ data: mockConfig.response(params), status: 200 });
}
// 未匹配到 mock 则走正常网络请求
}
```
**Mock 数据结构:**
```
mock/
├── index.js # 入口聚合,构建 URL → handler 映射表,提供 matchMock()
├── auth.js # /auth/login
├── common.js # getGoodsNum、getLanguage、getRuotes
├── home.js # getCarousel、boutiqueGoods、recommendGoods
├── classify.js # getClassify、classifyGoods
├── shop.js # getCard、changeGoods、delCard
├── address.js # getAddersslist
└── goods.js # getGoodsdetal
```
> Mock 数据覆盖 `js.md` 中定义的全部 15 个接口,切换后**全局生效**(登录页及后续所有页面的接口请求均走 mock 数据)。选择 link 模式时,接口地址恢复为 `.env` 文件中配置的 `VITE_BASE_URL`。
### 国际化
基于 `i18next` + `react-i18next`,支持本地语言包和远程接口动态覆盖:
```jsx
// 使用翻译
const { t } = useTranslation();
const title = t("common.home");
```
远程覆盖机制在应用初始化时自动执行,通过 `$globalConstant("resultCode").SUCCESS_CODE` 校验响应状态后加载远程语言包:
```js
// src/hooks/useCommon.js
const { getLanguage } = $globalServicer("common");
const [lang] = await getLanguage();
const { SUCCESS_CODE } = $globalConstant("resultCode");
if (lang?.code === SUCCESS_CODE) {
Object.keys(lang?.data).forEach((key) => {
i18n.addResourceBundle(key, "translation", lang?.data[key], true, true);
});
}
```
### 样式方案
- **CSS Modules** — `xxx.module.less` 文件自动开启局部作用域
- **:global 覆盖** — 在局部样式文件中用 `:global { }` 覆盖 UI 库样式
- **自动 rem 转换** — `postcss-pxtorem` (rootValue: 37.5) 自动将 px 转为 rem
- **norem 跳过** — 类名以 `norem` 开头的不进行 rem 转换
## 脚手架配置
### Vite 插件模块化
在 `vite-config/plugins/` 目录下创建 `.js` 文件即可自动加载,无需手动注册:
```js
// vite-config/index.js
const loadPluginModules = async () => {
const modulesPath = resolve(__dirname, "plugins");
const fileNames = fs.readdirSync(modulesPath);
// 自动 import plugins/ 下所有 .js 文件
};
```
### 自定义 Vite 插件
| 插件 | 说明 | 钩子 |
|------|------|------|
| `vite-plugin-copy` | 构建后将环境配置文件复制到 dist | closeBundle |
| `vite-plugin-zip` | 构建后将 dist 目录打包为 zip | closeBundle |
| `vite-plugin-legacy` | 生成低版本浏览器兼容包 | - |
| `vite-plugin-progress` | 构建时显示进度条 | - |
### 环境配置
```bash
vite-env/
├── .env # 本地环境(默认)
├── .env.dev # 测试环境(npm run build:dev)
└── .env.prod # 生产环境(npm run build:pro)
```
通过 `import.meta.env.VITE_BASE_URL` 在代码中访问环境变量。
## 相关链接
- [脚手架搭建 Demo (Vue/React + Vite/Farm/Rsbuild)](https://gitee.com/674074365/lan-project-cli)
- [Vue 版本项目演示](https://www.lanlianjiu.xyz/#/login)
- [Vue 版本项目代码](https://gitee.com/674074365/vue3-vant3-h5)
- [React 版本项目演示](http://www.react.lanlianjiu.xyz/#/login)
- [开发文档](http://www.reactappdoc.lanlianjiu.xyz)
## 贡献指南
1. Fork 本仓库
2. 创建特性分支 (`git checkout -b feat/your-feature`)
3. 提交变更 (`git commit -m 'feat: add your feature'`)
4. 推送分支 (`git push origin feat/your-feature`)
5. 创建 Pull Request
## License
[MIT](./LICENSE)