React高级 - 搭建网站
搭建网站
本文档主要从大方向规划网站的功能点,并逐个开发搭建完成。
首先,明确网站搭建需要实现的功能:
- 1、引入功能全面的 UI 组件库
- 2、封装: ajax 请求 / hooks / 组件 ...
- 3、配置多环境:本地 / 测试 / 预生产 / 开发 ...
- 4、自动化流程:测试 / 打包部署 ...
- 5、认证方式: jwt
- 6、其他配置:Typescript / Todo ...
然后就是针对上述功能点逐个进行实现。
项目默认基于 create-react-app 脚手架生成。
一、引入 【ant design】
js
// 1. 引入UI组件库
$ yarn add antd
// 2. 使用UI组件库
import { DatePicker } from 'antd';
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
ReactDOM.render(<DatePicker />, mountNode);
二、引入 【axios】
js
// 1. 引入 axios 依赖
$ yarn add axios
// 2. 配置 axios 相关文件
// utils/http 文件
// 配置【axios】的拦截和请求封装
// 前置拦截
// 后置拦截
// api/index 文件
// 引入 utils/http
// 编写具体的api接口地址
点击查看 utils/http.js 代码
js
/**
* 网络请求配置
*/
import axios from "axios";
import { API_PATH } from './config'
let env = API_PATH || '';
axios.defaults.timeout = 100000;
axios.defaults.baseURL = env || "http://www.ycy88.com/api";
/**
* http request 拦截器
*/
axios.interceptors.request.use(
(config) => {
console.log('-------------- Req 前置拦截器 ---------------------')
config.data = JSON.stringify(config.data);
config.headers = {
"Content-Type": "application/json",
};
return config;
},
(error) => {
return Promise.reject(error);
}
);
/**
* http response 拦截器
*/
axios.interceptors.response.use(
(response) => {
console.log('-------------- Res 后置拦截器 ---------------------')
if (response.data.errCode === 2) {
console.log("过期");
}
return response;
},
(error) => {
console.log('-------------- Res 后置拦截器 Error ---------------------')
console.log("请求出错:", error);
}
);
/**
* 封装get方法
* @param url 请求url
* @param params 请求参数
* @returns {Promise}
*/
export function get(url: string, params = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params,
}).then((response) => {
landing(url, params, response.data);
resolve(response.data);
})
.catch((error) => {
reject(error);
});
});
}
/**
* 封装post请求
* @param url
* @param data
* @returns {Promise}
*/
export function post(url: string, data: object) {
return new Promise((resolve, reject) => {
axios.post(url, data).then(
(response) => {
//关闭进度条
resolve(response.data);
},
(err) => {
reject(err);
}
);
});
}
/**
* 封装patch请求
* @param url
* @param data
* @returns {Promise}
*/
export function patch(url: string, data = {}) {
return new Promise((resolve, reject) => {
axios.patch(url, data).then(
(response) => {
resolve(response.data);
},
(err) => {
msgFunc(err);
reject(err);
}
);
});
}
/**
* 封装put请求
* @param url
* @param data
* @returns {Promise}
*/
export function put(url: string, data = {}) {
return new Promise((resolve, reject) => {
axios.put(url, data).then(
(response) => {
resolve(response.data);
},
(err) => {
msgFunc(err);
reject(err);
}
);
});
}
//统一接口处理,返回数据
const HTTP = function (fecth: string, url: string, param = {}) {
let _data = "";
console.log(_data)
return new Promise((resolve, reject) => {
switch (fecth) {
case "get":
console.log("begin a get request,and url:", url);
get(url, param)
.then(function (response) {
resolve(response);
})
.catch(function (error) {
console.log("get request GET failed.", error);
reject(error);
});
break;
case "post":
post(url, param)
.then(function (response) {
resolve(response);
})
.catch(function (error) {
console.log("get request POST failed.", error);
reject(error);
});
break;
default:
break;
}
});
}
//失败提示
function msgFunc(err: any) {
if (err && err?.response) {
switch (err.response.status) {
case 400:
alert(err.response.data.error.details);
break;
case 401:
alert("未授权,请登录");
break;
case 403:
alert("拒绝访问");
break;
case 404:
alert("请求地址出错");
break;
case 408:
alert("请求超时");
break;
case 500:
alert("服务器内部错误");
break;
case 501:
alert("服务未实现");
break;
case 502:
alert("网关错误");
break;
case 503:
alert("服务不可用");
break;
case 504:
alert("网关超时");
break;
case 505:
alert("HTTP版本不受支持");
break;
default:
}
}
}
/**
* 查看返回的数据
* @param url
* @param params
* @param data
*/
function landing(url: string, params = {}, data: any) {
if (data && data?.code === -1) {
}
}
export default HTTP;
三、引入【支持TS】
js
// 1. 安装typescript及声明类型
$ yarn add typescript @types/react @types/react-dom @types/node @types/jest
// 2. 配置tsconfig.json
// 其他: tsc --init 可以生成tsconfig.json
四、【配置多环境】
js
// 1. 安装dotenv:
$ yarn add dotenv-cli
// 2. 根目录创建env等文件: 【.env】、【.env.dev】、【.env.test】、【.env.prod】等
REACT_APP_API_PATH = 'http://ycy88.com/dev' // 比如 .env.dev 里面配置
// 3. src/utils目录下配置 【config.ts】
export const API_PATH = process.env.REACT_APP_API_PATH;
// 4. 修改package.json配置文件
"scripts": {
"serve": "dotenv -e .env.dev craco start",
"dev": "dotenv -e .env.dev craco start",
"start": "dotenv -e .env.dev craco start",
"build": "dotenv -e .env.prod craco build",
"build:dev": "dotenv -e .env.dev craco build",
"build:test": "dotenv -e .env.test craco build",
"test": "craco test",
"eject": "craco eject"
},
五、引入【redux】
配置编写 store 相关文件
js
// 1. 安装依赖:
$ yarn add react-redux
$ yarn add @reduxjs/toolkit
$ yarn add redux-persist // React-redux持久化
// 2. 创建 Redux 状态(单个)
// ====== src/store/counterSlice.js
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
// 3. 将单个Redux注入全局桶
// ====== store/index.js
import { configureStore } from '@reduxjs/toolkit'
import storage from 'redux-persist/lib/storage'
import {combineReducers} from "redux";
import { persistReducer } from 'redux-persist'
import thunk from 'redux-thunk'
import counterReducer from './counterSlice'
const reducers = combineReducers({
counter: counterReducer,
});
const persistConfig = {
key: 'root',
storage
};
const persistedReducer = persistReducer(persistConfig, reducers);
const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
middleware: [thunk]
});
export default store;
将store注入全局组件中
jsx
// ====== src/index.js // 入口文件
import React, { Suspense } from "react";
import { createRoot } from "react-dom/client";
// React-redux
import { Provider } from 'react-redux'
import store from './store'
// 持久化:本质还是把store存到 local Storage 里面
import { PersistGate } from 'redux-persist/integration/react'
import { persistStore } from 'redux-persist'
const root = createRoot(document.getElementById("root"));
// 持久化
let persistor = persistStore(store);
root.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
{/* ... */}
</PersistGate>
</Provider>
);
六、引入【react-router】
js
// 1. 安装依赖:
$ yarn add react-router-dom
// 2. 配置route :类似vue
// ====== src/routes/index.js
import React, { lazy } from "react";
// 懒加载路由
const Home = lazy(() => import('@/pages/Home'));
const About = lazy(() => import('@/pages/about/About'));
const routes = [
{
key: 'index',
path: '/',
element: <Home />
},
{
key: 'home',
path: '/home',
element: <Home />
},
{
key: '404',
path: '/*',
element: <About />
},
];
export default routes;
// ====== src/index.js // 完整的入口文件代码
import React, { Suspense } from "react";
import { createRoot } from "react-dom/client";
// React-redux
import { Provider } from 'react-redux'
import store from './store'
// 持久化:本质还是把store存到 local Storage 里面
import { PersistGate } from 'redux-persist/integration/react'
import { persistStore } from 'redux-persist'
// 全局样式
import '@/assets/scss/index.scss';
// React-router
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
import routes from './routes'
// 布局 & 路由页面
import Layout from '@/layout/Layout'
// 全局loading组件
import Loading from '@/components/loading/loading';
const root = createRoot(document.getElementById("root"));
// 路由表配置
const renderRoutes = (routes) => {
return routes.map(item => {
if (item && item.children) {
return (
<Route path={item.path} element={item.element} key={item.key}>
{ renderRoutes(item.children) }
</Route>
);
} else {
return (
<Route path={item.path} element={item.element} key={item.key}></Route>
);
}
});
};
// 持久化
let persistor = persistStore(store);
root.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Suspense fallback={<Loading />}>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
{ renderRoutes(routes) }
</Route>
</Routes>
</BrowserRouter>
</Suspense>
</PersistGate>
</Provider>
);
七、引入【scss】
js
// 1. 安装依赖:
$ yarn add sass node-sass@npm:sass
// 2. 文件名调整为: 【.scss】
八、路径别名配置
js
// 1. 安装依赖:
$ yarn add @craco/craco
// 2. 根目录下 新建 craco.config.js
const path = require('path')
const pathResolve = (pathUrl) => path.join(__dirname, pathUrl)
module.exports = {
webpack: {
// 设置别名
alias: {
'@': pathResolve('src'),
'@assets': pathResolve('src/assets'),
'@images': pathResolve('src/assets/image'),
'@components': pathResolve('src/components'),
'@hooks': pathResolve('src/hooks'),
'@pages': pathResolve('src/pages'),
'@utils': pathResolve('src/utils')
}
}
}
// 3. 配置package.json -- 与“配置多环境”相结合
"scripts": {
"serve": "dotenv -e .env.dev craco start",
"dev": "dotenv -e .env.dev craco start",
"start": "dotenv -e .env.dev craco start",
"build": "dotenv -e .env.prod craco build",
"build:dev": "dotenv -e .env.dev craco build",
"build:test": "dotenv -e .env.test craco build",
"test": "craco test",
"eject": "craco eject"
},
参考:Craco
九、项目完善: todo
案例源码:
React网站(园博吧)