一、项目起步
1.创建项目
$ npx create-react-app my_react
2、安装依赖
本项目用到的依赖可以预先安装好:
- antd
- redux与react-redux
- react-router-dom
- axios
$ npm i antd redux react-redux react-router-dom@6 axios --save
3.配置路由 (根据项目分析需要几个页面,以及页面之间的关系)
/*
App > List + Edit + Means
Login
Register
History模式 -- BrowserRouter
Hash模式 -- HashRouter
*/
import App from '../App'
import Edit from '../pages/Edit'
import List from '../pages/List'
import Means from '../pages/Means'
import Login from '../pages/Login'
import Register from '../pages/Register'
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'
const BaseRouter = () => (
<Router>
<Routes>
<Route path='/' element={<App />}>
<Route path='/edit' element={<Edit />}></Route>
<Route path='/list' element={<List />}></Route>
<Route path='/means' element={<Means />}></Route>
</Route>
<Route path='/login' element={<Login />}></Route>
<Route path='/register' element={<Register />}></Route>
</Routes>
</Router>
)
export default BaseRouter
或者也可以写成这样(与·vue写法相似)
import App from '../App'
import Edit from '../pages/Edit'
import List from '../pages/List'
import Means from '../pages/Means'
import Login from '../pages/Login'
import Register from '../pages/Register'
import {Navigate} from 'react-router-dom'
export default [
{ path:'/',
element:<App />,
children[
{
path:'edit',
element:<Edit />
},
{
path:'list',
element:<List/>
},
{
path:'means',
element:<Means />
}
]
},
{
path:'/login',
element:<Login />
},
{
path:'/register',
element:<Register />
}
]
4.确认使用框架,编写静态代码
UI框架使用:Ant Design
在入口文件index.js中:
import ReactDOM from 'react-dom'
import App from './App'
import 'antd/dist/antd.css';
ReactDOM.render(
<App />,
document.getElementById('root')
)
然后在App.jsx中:
import React from 'react'
import { Button } from 'antd';
export default function App() {
return (
<div>
<Button type="primary">Primary Button</Button>
</div>
)
}
如果可以看到这款颜色的按钮,即代表使用成功:
二、Request封装
1.在src下创建request目录,并在其中创建request.js及api.js
2.封装axios请求
(导入axios,定义并创建域名接口,然后共享全局变量)
request.js:
import axios from 'axios'
// 配置项
const axiosOption = {
baseURL: '域名' ,// 请求接口的域名
timeout: 5000
}
// 创建一个单例
const instance = axios.create(axiosOption);
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
let token = localStorage.getItem('cms-token')
if(token){
config.headers = {
'cms-token':window.localStorage.getItem('cms-token')
}
}
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response.data;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default instance;
3.api.js
指API接口,在此之前需要导入request文件在获取时候可以传入接口参数,get方法获取则定义为params方法,post方法则定义data方法
import request from './request'
export const getNewsAPI = function (接口参数) {
return request.get('接口的api', { params: { 接口参数 } }) // 多个参数用逗号隔开
}
4.在组件使用
methods: {
async initArticleList () {
const { data: res } = await getNewsAPI(与组件对接的参数) // 在此可以在组件中定义好参数方便存储
console.log(res.data)
}
4.解决跨域
在 react 17.x中
方法一:我们可以在package.json 中写
"proxy":{"这里写你要请求的地址"}
方案二(推荐):
先安装 http-proxy-middleware
:
npm install http-proxy-middleware
或者
yarn add http-proxy-middleware
这里注意,http-proxy-middleware 模块是有版本区别的,默认安装最新版本,然后在 src 目录下新建 setupProxy.js
:
const proxy = require('http-proxy-middleware');
// 这个玩意不用下,react里自己带了
module.exports = function(app) {
app.use(
proxy('/api1', { // 发送请求的时候 react会自动去找这个api1,匹配这个路径,然后去发送对的请求
target: 'http://localhost:5000',
changeOrigin: true, //控制服务器接收到的请求头中host字段的值
pathRewrite: {'^/api1': ''} // 跟上面匹配,这个api1只是找这个路径用的,实际接口中没有api1,所以找个目标地址后,要把api1给替换成空
}),
// changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
// changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
// 注意!!注意!!注意!! changeOrigin默认值为false,需要我们自己动手把changeOrigin值设为true
proxy('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}),
)
}
在react18中
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
createProxyMiddleware('/api1', {
target: 'http://localhost:5000',
changeOrigin: true,
pathRewrite: { '^/api1': '' }
}),
createProxyMiddleware('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: { '^/api2': '' }
}),
)
}
或者,还有另一种方法
如果你已经进行了 npm run eject(
会复制所有依赖文件和相应的依赖(webpack、babel等)到你的项目。是个单向的操作,一旦 eject ,npm run eject的操作是不可逆的)
,建议你直接修改 config>webpackDevServer.config.js
:
proxy: {
'/api': {
target: 'http://47.93.114.103:6688/manage', // 后台服务地址以及端口号
changeOrigin: true, //是否跨域
pathRewrite: { '^/api': '/' }
}
}
然后修改request.js:
const axiosOption = {
baseURL: '/api',
timeout: 5000
}
最后重新执行 npm run start
!
三.登陆页面重要逻辑部分(注册也一样)
Login.jsx:
import { Link, useNavigate} from 'react-router-dom'
//引入接口
import {LoginApi} from '../axios/api'
export default function Login() {
const navigate = useNavigate()
const onFinish = (values) => {
// console.log('Success:', values);
//使用APi请求数据
LoginApi({
username:values.username,
password:values.password
}).then(res=>{
console.log(res);
if(res.errCode===0){
message.success('登陆成功');
//存储数据(这里需要根据后端返回值来写)
localStorage.setItem('avatar',res.data.avatar)
localStorage.setItem('cms-token',res.data['cms-token'])
localStorage.setItem('editable',res.data.editable)
localStorage.setItem('player',res.data.player)
localStorage.setItem('username',res.data.username)
//跳转路径
navigate('/')
}else{
message.error('用户已存在');
}
})
};
四、路由跳转
使用useNavigate这个路由hook,可以实现路由跳转。若要判断这个路径如果为/,则自动跳转到/home,代码如下:
import React, {useEffect} from 'react'
import {Outlet, Link, useLocation, useNavigate} from 'react-router-dom'
function App() {
let {pathname} = useLocation()
const navigate = useNavigate()
useEffect(()=>{
if(pathname==='/'){
navigate('/home')
}
}, [])
return ...
}
export default App
五、useLocation
使用useLocation这个路由hook,可以获取地址栏路径:
import {useLocation} from 'react-router-dom'
function App() {
let {pathname} = useLocation() // 得到当前路径
}
六、参数携带
1、url子路由形式
通过在路由中配置子路由形式,可以实现参数传递。如给/list携带参数,以这个形式:/list/123。
// 路由文件
<Route path='/list/:id' element={<List />}></Route>
// App.jsx中
<li><Link to="/list/123">列表页</Link></li>
如果想要获取参数的值,可以使用useParams这个路由hook:
import React from 'react'
import {useParams} from 'react-router-dom'
export default function List() {
const params = useParams()
console.log(params) // {id: '123'}
return <h2>List列表页</h2>
}
2、问号?形式
地址栏携带参数还可以通过问号形式:
<li><Link to="/detail?id=456">详情页</Link></li>
获取该参数的方式,需要使用useSearchParams这个路由hook,并且调用getAll方法才能获取得到:
import React from 'react'
import {useSearchParams} from 'react-router-dom'
export default function Detail() {
const [params] = useSearchParams()
console.log(params.getAll('id')) // ['456']
return <h2>Detail详情页</h2>
}
3、state携带
以上两种携带参数的方式,都只能携带简单的参数,如果数据量较大,其实并不方便,因此,在使用useNavigate()这个hook实现跳转时,其实也可以携带参数:
navigate('/home', {
state: {id: 789}
})
获取该参数的方式便是使用useLocation这个路由hook:
七、分页步骤
1.antd组件table内置了分页效果
return (
<div className='list_table'>
<Table
showHeader={false}
columns={columns}
dataSource={arr}
onChange={pageChange} //分页函数
//分页效果,pagination值是动态可操作的,所以用useState来管理
pagination={pagination}
/>
</div>
)
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
//current、pageSize、total这三项是需要做配置的
配置完成以后,当点击下一页的时候,就可以得到那三个参数最新的值
const pageChange = (arg) => getArticleList(arg.current, arg.pageSize);
然后再一次的做请求,通过setPagination修改 useState里面的值
// 提取请求的代码
const getArticleList = (current, pageSize) => {
ArticleListApi({
num: current,
count: pageSize
}).then(res => {
if (res.errCode === 0) {
// 更改pagination
let { num, count, total } = res.data;
setPagination({ current: num, pageSize: count, total })
// 深拷贝获取到的数组
let newArr = JSON.parse(JSON.stringify(res.data.arr));
// 声明一个空数组
let myarr = []
/*
1. 要给每个数组项加key,让key=id
2. 需要有一套标签结构,赋予一个属性
*/
newArr.map(item => {
let obj = {
key: item.id,
date: moment(item.date).format("YYYY-MM-DD hh:mm:ss"),
mytitle: <MyTitle id={item.id} title={item.title} subTitle={item.subTitle} />
}
myarr.push(obj)
})
setArr(myarr)
}
})
}
上面这一行就是再次请求