React 07.
拓展开发1、复习axios react redux
项目管理
2、计算属性:自定义函数、reselect
、repure
3、调试工具:react-devtools
4、异步请求辅助函数:react-thunk
创建项目
$ npx create-react-app app01
项目降级
$ npm i react@17 react-dom@17 -S
修改启动文件:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(
<App></App>,
document.querySelector("#root")
)
安装项目依赖:
$ npm i react-router-dom@5 redux react-redux axios antd --force -S
① 封装异步请求模块src/plugins/http.js
/** 异步请求模块 */
import { Component } from 'react'
import axios from "axios";
const instance = axios.create({
baseURL: 'http://localhost:9527'
})
instance.interceptors.request.use( request => {
return request
})
instance.interceptors.response.use( response => {
return response.data
})
Component.prototype.$http = instance
export default instance
主模块src/index.js
中引入生效
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import './plugins/http' // 引入异步请求模块
ReactDOM.render(
<App></App>,
document.querySelector("#root")
)
② 封装路由模块
src/router/index.js
:路由映射规则
/** 路由映射规则配置文件 */
const routes = [
]
export default routes
src/router/RouterView.jsx
:路由组件
import React, {Suspense} from 'react'
// import { Spin } from 'antd';
import {
Route,
Redirect,
Switch
} from 'react-router-dom'
export default function RouterView(props) {
const { routes } = props
return (
{
routes.map(route => {
if(route.redirect) {
// 重定向
return
} else {
// 正常跳转
return {
return
}}>
}
})
}
)
}
③ ant design
封装
建议参考官方文档,使用高级语法进行封装
安装依赖(核心库、图标库)
$ npm i antd @ant-design/icons --force -S
安装高级配置依赖
$ npm i @craco/craco craco-less --force -S
添加配置文件:app01/craco.config.js
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},
],
};
修改启动配置文件:package.json
...
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "craco eject"
},
...
修改src/App.css
重命名src/App.less
,在App.js
中引入
import './App.less';
function App() {
return (
<div className="App">
<h2>入口模块</h2>
</div>
);
}
export default App;
小总结:
项目初始化过程,主要包含了如下步骤:
1、创建项目,项目降级
2、安装
axios
封装异步请求模块3、安装
react-router-dom
封装路由模块4、安装
ant design
,通过craco
方式,封装界面组件库
后台管理系统,区分一级路由组件和嵌套路由组件
① 分析组件构成
src/
views/
Login/
Reg/
Main/
Home/
Menus/
Roles/
Users/
Brand/
Goods/
About/
② 创建组件
③ 添加路由规则
/** 路由映射规则配置文件 */
import {lazy} from 'react'
// 项目展示的第一个页面,直接导入
import Login from '../views/Login'
const routes = [
{
name: 'r',
path: '/',
redirect: '/login',
exact: true
},
{
name: 'Login',
path: '/login',
component: Login,
exact: false
},
{
namt: 'Reg',
path: '/reg',
component: lazy( () => import('../views/Reg') ),
exact: false
},
{
name: 'Main',
path: '/main',
component: lazy( () => import('../views/Main')),
exact: false,
children: [
{
name: 'Home',
path: '/main/home',
component: lazy( () => import('../views/Home')),
exact: false,
},
{
name: 'Menus',
path: '/main/menus',
component: lazy( () => import('../views/Menus')),
exact: false,
},
{
name: 'Roles',
path: '/main/roles',
component: lazy( () => import('../views/Roles')),
exact: false,
},
{
name: 'Users',
path: '/main/users',
component: lazy( () => import('../views/Users')),
exact: false,
},
{
name: 'Brand',
path: '/main/brand',
component: lazy( () => import('../views/Brand')),
exact: false,
},
{
name: 'Goods',
path: '/main/goods',
component: lazy( () => import('../views/Goods')),
exact: false,
},
{
name: 'About',
path: '/main/about',
component: lazy( () => import('../views/About')),
exact: false,
}
]
}
]
export default routes
④ src/App.js
配置一级路由
import './App.less';
import {
BrowserRouter as Router
} from 'react-router-dom'
import routes from './router';
import RouterView from './router/RouterView'
function App() {
return (
);
}
export default App;
⑤ src/views/Main.js
配置嵌套路由
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
import { Space } from 'antd'
import RouterView from '../../router/RouterView'
export default class Main extends Component {
render() {
return (
首页
菜单管理
角色管理
用户管理
品牌管理
商品管理
关于我们
)
}
}
① 登录页面
UI
设计稿import React, { Component } from 'react'
import { Button, Form, Input, Card } from 'antd';
import { Link } from 'react-router-dom'
import './Login.less'
export default class Login extends Component {
onFinish(values) {
console.log('Success:', values);
};
onFinishFailed(errorInfo) {
console.log('Failed:', errorInfo);
};
render() {
return (
注册}>
)
}
}
② 注册页面
UI
设计稿③ 后台首页
① 通过redux
模拟管理数据
react
组件生命周期,模拟虚拟数据,将虚拟数据通过actionCreators函数添加到状态管理中
componentDidMount() {
// 初始化模拟数据
const initData = [
{id: 2, bname: "华为", bprice: 2999},
{id: 1, bname: "中兴", bprice: 1999}
]
// 调用actionCreator初始化数据
this.props.brandInitAction(initData)
}
② axios
异步请求数据
react
组件生命周期中,发送异步请求,获取数据,并通过actionCreators
函数初始化数据
react
组件管理,所有数据的CRUD
实际操作应该由redux
完成componentDidMount() {
// axios请求数据
// 思考:这里发送异步请求,请求数据,完成数据初始化,合适吗?
this.$http.get('/brand/list').then(response => {
if(response.code === 200) {
// 初始化异步数据
this.props.brandInitAction(response.data)
}
})
}
③ 完善异步请求过程
项目中添加redux-thunk
支持,让actionCreators
中的action
函数可以返回可执行的函数,我们就可以在actionCreators
中添加异步请求操作
# 编辑 src/store/index.js,添加redux-thunk支持
/** 状态管理模式 */
import {
legacy_createStore as createStore,
combineReducers,
applyMiddleware // 1.中间件支持模块
} from 'redux'
import thunk from "redux-thunk" //2. 异步支持模块
import brandReducer from './brand/brandReducer'
const store = createStore( combineReducers({
brand: brandReducer
}), applyMiddleware(thunk) ) // 3.给store添加异步请求模块支持
export default store
# 编辑 src/store/brand/actionCreators.js, 添加异步请求初始化数据的操作
import {....} from './actionType'
import axios from '../../plugins/http'
....
// export const brandInitAction = data => ({type: brandInitType, data})
export const brandInitAction = () => {
// redux-thunk 允许actionCreators返回一个函数(自动执行函数)
return dispatch => {
// 如果让redux actionCreators添加异步操作,需要中间件支持(redux-thunk)
axios.get('/brand/list').then(response => {
if(response.code === 200) {
return dispatch({type: brandInitType, data: response.data})
}
})
}
}
一般前后端分离项目中,后端接口的地址和url
路径经常发生变化,如果我们将异步请求过程直接写在redux
中的actionCreators
的函数中,后期的维护工作量非常庞大!
所以前端项目中,我们会创建一个专门处理异步请求的独立模块,负责通过异步函数获取对应的数据,并且给前端应用提供固定的函数名称;如果后端接口后期出现需求变动,前端项目只需要维护请求模块即可!
创建的请求模块:src/request/brand.js
import axios from '../plugins/http'
// 导出一个查询品牌列表的异步函数
export const getBrandList = async () => await axios.get('/brand/list')
// ...
重构actionCreators
处理函数,编辑src/store/brand/actionCreators.js
import {
brandAddType,
brandEditType,
brandDelType,
brandInitType
} from './actionType'
// import axios from '../../plugins/http'
// 导入异步请求模块
import { getBrandList } from '../../request/brand'
export const brandAddAction = data => ({type: brandAddType, data})
export const brandEditAction = data => ({type: brandEditType, data})
export const brandDelAction = data => ({type: brandDelType, data})
// export const brandInitAction = data => ({type: brandInitType, data})
export const brandInitAction = () => {
// redux-thunk 允许actionCreators返回一个函数(自动执行函数)
return async dispatch => {
// 使用封装的异步请求模块处理数据
let result = await getBrandList()
dispatch({type: brandInitType, data: result.data})
}
}
react
项目,同样支持浏览器插件调试功能,通过图形化界面的方式观察数据以及数据的操作流程
react-devtools
,安装在浏览器上react-devtools-extension
,需要添加到代码中redux-devtools
打开浏览器的管理插件面板,将插件添加到浏览器中,便于测试
redux-devtools-extension
项目调试模块,安装
$ npm i redux-devtools-extension -S
编辑数据模块:src/store/index.js
,添加调试支持
/** 状态管理模式 */
import {
legacy_createStore as createStore,
combineReducers,
applyMiddleware
} from 'redux'
// 1.>>>>>>>>>>>>>>导入数据调试模块
import { composeWithDevTools} from "redux-devtools-extension"
import thunk from "redux-thunk"
import brandReducer from './brand/brandReducer'
// 2.>>>>>>>>>>>>>>将数据调试模块,挂载到store实例上
const store = createStore( combineReducers({
brand: brandReducer
}), composeWithDevTools(applyMiddleware(thunk)) )
export default store
重启浏览器,访问当前react
项目:检查窗口中可以通过可视化界面监控项目中的数据
reselect
Vuex
中有一个getters
模块,可以用于自动运算,类似计算属性的功能
react
中第三方提供了一个用于缓存数据计算的模块:reselect
reselector X
$ npm i reselect -S
创建一个缓存结果的操作函数,不论在任何时候调用,如果参与运算的数据没有发生变化,直接返回上一次缓存的运算结果:(和计算属性非常类似)
import {createSelect} from "reselect"
// 创建一个缓存数据的函数
let catchFn = createSelect(function(dataList){
// 数据准备
return dataList
}, (dataList) => {
// 数据运算:运算结果会被缓存
let res = dataList.filter(item => item.bprice > 6000)
this.setState({
filterBrands: res
})
})
repure
参考http://npmjs.org
,repure
模块的使用