react自定义应用封装
基于create-react-app搭建自定义应用
本文是基于 create-react-app搭建自定义应用,配置路由、less、axios请求封装和变量环境等...。 供自己以后查漏补缺,也欢迎同道朋友交流学习。
环境准备
- node版本:v18.12.0
- yarn版本:v1.22.17
react相关应用版本
- "react":"^18.3.1"
- "react-scripts": "5.0.1"
- "react-router-dom": "^6.23.1"
- "@craco/craco": "^7.1.0"
- "antd": "^5.17.4"
- "mobx": "^6.12.3"
- "mobx-react": "^9.1.1"
基础搭建
使用yarn进行基础构建
yarn create react-app my-app
# 运行项目, 默认生成一个3000端口的页面
cd my-app
yarn start
得到一个官方标准的文件结构:
.
├── README.md
├── package.json
├── public
├── src
│ ├── App.css // css可以清空
│ ├── App.js
│ ├── App.test.js // 没有单元测试可以删除
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js // 不用也可以删除
│ └── setupTests.js // 不用也可以删除
└── yarn.lock
官网案例的缺陷
这个文件目录可以看出来,官方案例只能做一个简单的demo,不能作为一个企业标准应用,缺失很多:
- 没有路由模块
- 没有less(或Sass)样式支持
- 没有UI组件库
- 没有封装请求库
- 没有状态共享库
- 配置环境变量
增加路由模块
安装路由
yarn add react-router-dom
修改APP.js,配置路由组件:
import { BrowserRouter } from "react-router-dom";
import AppRoutes from "./routes";
function App() {
return (
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
);
}
export default App;
创建routes目录存储路由:
# 创建目录
mkdir routes && cd routes
# 新建index.js文件
touch index.js
路由配置列表和详情页:
import { Routes, Route, Navigate } from 'react-router-dom';
import List from "../pages/List";
import Detail from "../pages/Detail";
const AppRoutes = () => {
return (
<Routes>
<Route path="/" exact element={<Navigate to="/list" replace />} />
<Route path="/list" exact Component={List} />
<Route path="/detail/:id" exact Component={Detail} />
</Routes>
);
};
export default AppRoutes;
src下新增page目录存储页面如下:
# 创建页面根目录
mkdir pages && cd pages
# 创建列表和详情页
mkdir List Detail
# 创建页面和样式
cd List
touch index.jsx
cd Detail
touch index.jsx
# 目录结构
pages
├── Detail
│ ├── index.jsx
└── List
├── index.jsx
列表页:
import { useState, useEffect } from "react";
import { useNavigate } from 'react-router-dom';
const List = () => {
const navigate = useNavigate();
const [listData, setListData] = useState([]);
useEffect(() => {
setListData([
{ id: 1, title: "新闻标题11111", date: "2024-05-30 11:01:00" },
{ id: 2, title: "新闻标题22222", date: "2024-05-30 12:02:00" },
{ id: 3, title: "新闻标题33333", date: "2024-05-30 13:03:00" },
]);
}, []);
const goToDetail = (id) => {
navigate(`/detail/${id}`)
};
return (
<div>
{listData.map((item) => (
<div key={item.id}>
<div style={{ color: "blue" }} onClick={() => goToDetail(item.id)}>
{item.title}
</div>
<div>{item.date}</div>
</div>
))}
</div>
);
};
export default List;
详情页:
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
const detailData = {
1: {
id: 1,
title: "新闻标题11111",
date: "2024-05-30 11:01:00",
content: "新闻内容11111",
},
2: {
id: 2,
title: "新闻标题22222",
date: "2024-05-30 12:02:00",
content: "新闻内容22222",
},
3: {
id: 3,
title: "新闻标题33333",
date: "2024-05-30 13:03:00",
content: "新闻内容33333",
},
};
const Detail = () => {
const { id } = useParams();
const [detail, setDetail] = useState(null);
useEffect(() => {
setDetail(detailData[id] || null);
}, [id]);
return (
<div>
{detail ? (
<div>
<div>{detail.title}</div>
<div>{detail.date}</div>
<div>{detail.content}</div>
</div>
) : (
<div>新闻不存在</div>
)}
</div>
);
};
export default Detail;
增加样式支持
这里使用less来做样式支持,也支持css modules
# 使用craco来修改webpack的配置:首先,你需要安装 @craco/craco 包
yarn add @craco/craco
# 安装 craco-less 插件以支持 Less
yarn add craco-less less less-loader --dev
根目录配置craco.config.js
文件以支持craco-less
,同时也配置css modules
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' }, // 自定义Ant Design主题变量,可选
javascriptEnabled: true,
},
},
},
},
],
style: {
modules: true,
// 其他样式配置,比如postcss等...
}
};
安装UI组件库
这里引入antd5和使用刚配置好的less美化页面UI:
# 引入antd
yarn add antd
# 引入craco-alias起别名
yarn add craco-alias --dev
修改craco.config.js
增加起别名配置
const CracoAliasPlugin = require('craco-alias');
module.exports = {
plugins: [
{
plugin: CracoAliasPlugin,
options: {
aliases: {
'@': 'src'
}
},
},
// ...
],
// ...
};
修改package.json
更新scripts
部分,使用 craco
替换 react-scripts
:
{
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
// ...其他脚本保持不变
},
},
列表页美化
修改List/index.jsx
:
import { Card, Button } from "antd";
import styles from "./index.module.less";
// ...引入其他组件
const List = () => {
// ...js部分
return (
<div className={styles.wrapper}>
{listData.map((item) => (
<Card
key={item.id}
title={item.title}
extra={<div>{item.date}</div>}
style={{ marginBottom: 20 }}
>
{item.desc}...
<Button type="link" onClick={() => goToDetail(item.id)}>
More
</Button>
</Card>
))}
</div>
);
};
export default List;
新建List/index.module.less
:
.wrapper {
width: 600px;
margin: 100px auto;
}
详情页美化
修改Detail/index.jsx
:
import styles from "./index.module.less";
// ...引入其他组件
const Detail = () => {
// ...js部分
return (
<div>
{detail ? (
<div className={styles.container}>
<div className={styles.title}>{detail.title}</div>
<div className={styles.date}>{detail.date}</div>
<div className={styles.content}>{detail.content}</div>
</div>
) : (
<div>新闻不存在</div>
)}
</div>
);
};
export default Detail;
新建Detail/index.module.less
:
.container {
width: 800px;
margin: 100px auto;
.title {
font-size: 20px;
font-weight: 600;
padding: 5px 0;
border-bottom: 1px solid #666;
margin-bottom: 20px;
}
.date {
font-size: 12px;
color: #999;
margin-bottom: 20px;
}
}
封装请求库
安装axios
yarn add axios
src下新建http.js
import axios from "axios";
import { message } from "antd";
const baseURL = "xxx";
// 设置基础URL
axios.defaults.baseURL = baseURL;
// 创建一个 Axios 实例
const instance = axios.create({
// 可以在这里设置默认的 headers、超时时间等
timeout: 10000,
});
// 请求拦截器,可以用来添加token等操作
instance.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么,比如添加认证信息
// config.headers.Authorization = `Bearer ${yourToken}`;
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 响应拦截器,统一处理错误
instance.interceptors.response.use(
async (response) => {
// 非200的错误处理
if (response?.data?.code !== "200") {
message.error(response?.data?.msg);
return response.data;
}
return response?.data?.result || null;
},
(error) => {
// 处理错误响应,比如弹出错误提示、跳转登录页等
const msg = error.response ? error.response.data : error.message;
console.error("Axios error:", msg);
message.error(msg);
return Promise.reject(error);
}
);
// 封装 GET 请求
export const get = (url, params) => instance.get(url, { params });
// 封装 POST 请求
export const post = (url, data, config) => instance.post(url, data, config);
src新建service
目录,目录里新建index.js
:
import { post } from '../http';
/**
* 调用某个接口
* @param {*} params
* @returns
*/
export const getSomeData = (params) => {
let url = 'aaa/bbb';
return post(url, params);
}
封装共享状态库
使用mobx
作为我们的共享状态库是因为mobx基于观察者模式,使用简单,几乎无侵入性。通过自动追踪依赖来更新视图,使得状态管理更加直观和容易。特别适合中型项目或对性能要求较高的应用,比redux
更轻量。
安装mobx
yarn add mobx mobx-react mobx-react-lite
src下面新建store
文件目录,并创建一个counterStore.js
文件:
cd src && mkdir store
cd store && touch counterStore.js
编辑counterStore.js
文件:
import { makeObservable, observable, action } from "mobx";
class CounterStore {
count = 0;
constructor() {
makeObservable(this, {
count: observable,
increment: action,
decrement: action,
});
}
increment() {
this.count++;
}
decrement() {
this.count--;
}
}
const counterStore = new CounterStore();
export default counterStore;
在List/index.jsx
页面使用
import { Card, Button } from "antd";
import { observer } from "mobx-react-lite";
import counterStore from "@/store/counterStore";
// ...其他引入
const List = () => {
// ...其他逻辑
return (
<div className={styles.wrapper}>
// 其他UI
<div>
mobx 案例 Count: {counterStore.count}
<Button
style={{ marginLeft: 5, marginRight: 5 }}
type="primary"
onClick={() => counterStore.increment()}
>
+
</Button>
<Button type="primary" onClick={() => counterStore.decrement()}>
-
</Button>
</div>
</div>
);
};
// 这里要注意用observer包一下
export default observer(List);
配置变量环境
我们在实际开发环境一般分为测试、UAT、准生产、生产等多个环境,调用不同的服务地址。通过配置环境变量可以自动打包到对应环境
安装依赖
yarn add dotenv-webpack --dev
修改craco.config.js
:
const Dotenv = require('dotenv-webpack');
module.exports = {
plugins: [
{
plugin: Dotenv,
// 动态加载 .env 文件
options: {
path: `.env.${process.env.REACT_APP_ENV}`, // 根据 process.env.NODE_ENV 动态加载 .env 文件
},
},
// ...
],
};
配置.env文件,根目录新建.env.development
,.env.test
,.env.uat
,.env.production
:
// .env.development文件里配置
REACT_APP_ENV=development
// .env.test文件里配置
REACT_APP_ENV=test
// .env.uat文件里配置
REACT_APP_ENV=uat
// .env.production文件里配置
REACT_APP_ENV=production
修改package.json
:
{
"scripts": {
"build:test": "REACT_APP_ENV=test craco build",
"build:uat": "REACT_APP_ENV=uat craco build",
"build:prod": "REACT_APP_ENV=production craco build",
// ...其他命令
},
// ...
}
src下新建config.js
做baseURL的配置:
const apiMap = {
development: "https://xxx-test.xxx.com",
test: "https://xxx-test.xxx.com",
uat: "https://xxx-uat.xxx.com",
production: "https://xxx-uat.prod.com",
};
console.log("@@@ process.env.REACT_APP_ENV", process.env.REACT_APP_ENV);
export const env = process.env.REACT_APP_ENV || "test";
export const baseURL = apiMap[env];
修改http.js
替换掉之前写死的baseURL:
import { baseURL } from "@/config";
// 设置基础URL
axios.defaults.baseURL = baseURL;
// ...
案例地址
转载自:https://juejin.cn/post/7375810931097976895