一、搭建项目
项目创建
npx create-react-app 项目名
清理项目多余文件
引入文件.prettierrc.json
{
"singleQuote": true,
"semi": false,
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "auto",
"trailingComma": "all",
"tabWidth": 2
}
引入重置样式文件 reset.css
/* 重置reseet */
html, body, h1, h2, h3, h4, h5, h6, div, dl, dt, dd, ul, ol, li, p, blockquote, pre, hr, figure, table, caption, th, td, form, fieldset, legend, input, button, textarea, menu {
margin: 0;
padding: 0;
}
header, footer, section, article, aside, nav, hgroup, address, figure, figcaption, menu, details {
display: block;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
caption, th {
text-align: left;
font-weight: normal;
}
html, body, fieldset, img, iframe, abbr {
border: 0;
}
html {
font-family: sans-serif;
}
i, cite, em, var, address, dfn {
font-style: normal;
}
[hidefocus], summary {
outline: 0;
}
li {
list-style: none;
}
h1, h2, h3, h4, h5, h6, small {
font-size: 100%;
}
sup, sub {
font-size: 83%;
}
pre, code, kbd, samp {
font-family: inherit;
}
q:before, q:after {
content: none;
}
textarea {
overflow: auto;
resize: none;
}
label, summary {
cursor: default;
}
a, button {
cursor: pointer;
}
h1, h2, h3, h4, h5, h6, em, strong, b {
font-weight: bold;
}
del, ins, u, s, a, a:hover {
text-decoration: none;
}
body, textarea, input, button, select, keygen, legend {
font: 14px/1.14 arial,\5b8b\4f53;
color: #333;
outline: 0;
}
body {
background: #fff;
}
a, a:hover {
color: #333;
}
在index.js引入
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './reset.css' //引入重置样式
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
引入sass
npm i sass
创建views/Home/index.jsx的单独scss文件
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { plus } from '@/store/slice/counter'
import styles from './index.module.scss' //引用
import { Button } from 'antd'
export default function Index() {
const num = useSelector(state => state.counter.count)
const dispatch = useDispatch()
return (
<div>
<h2 className={styles['title']}>主界面Home</h2> //使用
<p>num: {num}</p>
<button
onClick={() => {
dispatch(plus())
}}
>
加一
</button>
<Button type="primary">Primary Button</Button>
</div>
)
}
views/Home/index.module.scss
.title{
color: blueviolet;
}
路由
npm i react-router-dom@6.4.3
创建文件views
About/index.jsx
import React from "react";
export default function Index() {
return (
<div>
<p>路由1</p>
</div>
);
}
Home/index.jsx
import React from "react";
export default function Index() {
return (
<div>
<p>路由2</p>
</div>
);
}
创建路由文件router/index.jsx
import React from 'react'
import { BrowserRouter, Routes, Route, NavLink,Navigate } from 'react-router-dom'
import About from '../views/About'
import Home from '../views/Home'
export default function Index() {
return (
<BrowserRouter>
<NavLink to="/about">关于</NavLink> | <NavLink to="/home">home</NavLink>
<Routes>
<Route index element={<Navigate to='/about'></Navigate>}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/home" element={<Home />}></Route>
</Routes>
</BrowserRouter>
)
}
在app.js引入router/index.js
import Router from './router'
function App() {
return <Router></Router>
}
export default App
redux状态管理
npm i redux@4.1.1 --save
npm i redux-thunk@2.4.1
npm i @reduxjs/toolkit@1.9.0
npm install react-redux
或者:
npm i react-router-dom@6.4.3 redux@4.2.0 @reduxjs/toolkit@1.9.0 react-redux@8.0.5 -D
创建stroe/index.jsx
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './slice/counter'
export default configureStore({
reducer: {
counter: counterReducer,
},
})
创建stroe/slice/counter.jsx
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: {
count: 0,
},
reducers: {
plus: state => {
state.count += 1
},
},
})
export const { plus } = counterSlice.actions
export default counterSlice.reducer
在views/Home/index.jsx中使用
import React from 'react'
import { useSelector, useDispatch } from 'react-redux' //使用
import { plus } from '@/store/slice/counter'
import styles from './index.module.scss'
import { Button } from 'antd'
export default function Index() {
const num = useSelector(state => state.counter.count)
const dispatch = useDispatch()
return (
<div>
<h2 className={styles['title']}>主界面Home</h2>
<p>num: {num}</p>
<button
onClick={() => {
dispatch(plus()) //使用
}}
>
加一
</button>
<Button type="primary">Primary Button</Button>
</div>
)
}
引入antd组件
npm i antd@4.24.5
引人axios库
npm i axios
创建api文件
axios二次封装api/ request.js
import axios from 'axios'
import {message} from 'antd'
// export const baseURL = 'http://43.136.34.132:8088'
export const baseURL = 'http://localhost:3000'
// 创建axios新实例
const axiosInstance = axios.create({
baseURL, // 接口根地址
// baseURL: 'http://localhost:8080', // 接口根地址
timeout: 3000, // 超时时间
})
/**
* 请求拦截器
*/
axiosInstance.interceptors.request.use(
config => {
// 发起请求之前做一些处理
const token = localStorage.getItem('TOKEN')
if(token){
config.headers['Authorization'] = token
}
return config
},
err => Promise.reject(err)
)
/**
* 响应拦截器
*/
axiosInstance.interceptors.response.use(
res => {
// 响应数据做一些处理
// console.log('响应数据 res ', res)
return res.data
},
error => {
const { response } = error
if (response) {
const status = response.status
switch (status) {
case 404:
message.error('资源不存在 404')
break
case 401:
message.error('Unauthorized 身份验证凭证缺失!')
break
case 403:
message.error('403 Forbidden - 拒绝访问!')
break
case 500:
message.error('服务器出错')
break
default:
message.error('出现异想不到的错误!')
break
}
} else {
// 说明服务器连结果都没有返回,可能的原因有两种:
/**
* 1. 服务器崩掉了
* 2. 前端客户端断网状态
*/
if (!window.navigator.onLine) {
// 判断为断网,可以跳转到断网页面
message.error('网络不可用,请检查您的网络连接!')
return
} else {
message.error('连接服务端出错!' + error?.message)
return Promise.reject(error)
}
}
return Promise.reject(error)
}
)
export default axiosInstance
使用实例化
import axiosInstance from '../request'
/**
* 登录接口
*/
export const RequestLogin = (username, password) => {
return axiosInstance({
method: 'post',
url: '/api/login',
data: {
username,
password,
},
})
}
react配置文件
npm i react-app-rewired@2.2.1 customize-cra@1.0.0 -D
创建config-overrides.js
const { override, addWebpackAlias } = require('customize-cra')
const path = require('path')
module.exports = override(
// 路径别名
addWebpackAlias({
'@': path.resolve(__dirname, 'src'),
})
)
修改package.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
改为:
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
配置代理
npm i http-proxy-middleware@2.0.6 --save-dev
创建src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
createProxyMiddleware('/api', {
target: 'http://10.7.172.59:8089/',
changeOrigin: true,
// pathRewrite: {
// '^/api': ''
// }
})
)
}
完成后的package.json
{
"name": "react-app-template",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"antd": "^4.24.5",
"axios": "^1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@reduxjs/toolkit": "^1.9.0",
"customize-cra": "^1.0.0",
"http-proxy-middleware": "^2.0.6",
"react-app-rewired": "^2.2.1",
"react-redux": "^8.0.5",
"react-router-dom": "^6.4.3",
"redux": "^4.2.0",
"sass": "^1.63.6"
}
}