内容:路由、状态管理
日期:2020-09-14
一、路由
react也是spa单页面应用
1.安装
npm i react-router-dom --save
2.基本使用
(1)在/src/index.js引入路由模式
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>,document.getElementById('root'));
(2)创建几个页面组件,此处省略
(3)在/src/App.js中引入Switch、Route组件和需要展示的页面组件
import React from 'react';
import { Switch,Route } from 'react-router-dom'//引入需要的路由相关组件
import Home from './Home'//引入页面组件
import Login from './Login'
function App() {
return (
<div className="App">
{/* Switch相当于vue-router中的router-view */}
<Switch>
{/* Route是react中的路由配置规则 */}
<Route path="/home" component={ Home }></Route>
<Route path="/login" component={ Login }></Route>
</Switch>
</div>
);
}
export default App;
3.路由模式
BrowserRouter 是history模式
HashRouter是hash模式
在/src/index.js中引入hash模式
import { HashRouter } from 'react-router-dom'
ReactDOM.render(<HashRouter><App /></HashRouter>,document.getElementById('root'));
4.路由重定向
Redirect
import { Switch,Route,Redirect } from 'react-router-dom'
<Switch>
{/* Route是react中的路由配置规格 */}
<Route path="/home" component={ Home }></Route>
<Route path="/login" component={ Login }></Route>
{/* Redirect是重定向的组件
path属性的值为星号,表示前面所有的路由规则都没有匹配到
to属性的值为具体的某一个路由规则的path属性值
*/}
<Redirect path="*" to="/home"></Redirect>
</Switch>
5.路由导航
(1)内置组件
Link 会生成a标签,需要设置to属性,但是它没有激活状态的类名
NavLink 会生成a标签,需要设置to属性,它有激活状态的类名
引入导航组件
import { Switch,Route,Redirect,Link,NavLink } from 'react-router-dom'
使用导航组件
<Link to="/home">首页</Link>
<Link to="/login">登录</Link>
<hr/>
<NavLink to="/home">首页</NavLink>
<NavLink to="/login">登录</NavLink>
(2)编程式导航
当使用了react-router-dom路由后,在页面组件的props.history中封装一些可以使用方法,进行页面跳转,其中push、replace、go的用法和vue-router中一样
push
replace
go 需要传递参数,一般写-1,表示回退到上一个页面
goBack 不需要传递参数,直接回退到上一个页面
6.路由嵌套
需要在哪个页面展示不同的内容,就在哪个页面组件中引入Switch、Route来设置路由出口并定义路由规则。
(1)在src/App.js中定义一级路由规则
<Switch>
{/* Route是react中的路由配置规格 */}
<Route path="/home" component={ Home }></Route>
<Route path="/login" component={ Login }></Route>
<Redirect path="*" to="/home"></Redirect>
</Switch>
(2)在右侧展示的页面组件中定义二级路由规则
import React from 'react'
import { Switch,Route } from 'react-router-dom'
import Menu from './Menu'//引入左侧菜单组件
//引入需要在右侧展示的页面组件
import Student from '../pages/Student'
import Setting from '../pages/Setting'
import User from '../pages/User'
export default ()=>{
return(
<div className="middle">
<Menu></Menu>
<div className="right">
<Switch>
<Route path="/home/student" component={ Student }></Route>
<Route path="/home/setting" component={ Setting }></Route>
<Route path="/home/user" component={ User }></Route>
</Switch>
</div>
</div>
)
}
7.高阶组件
如果某个组件不是通过路由渲染出来的,在这个组件中是没有路由相关的信息的
withRouter,可以让页面组件的组成部分组件获取对应的路由信息
import React, { Component } from 'react'
//引入withRouter组件
import { NavLink,withRouter } from 'react-router-dom'
class Menu extends Component {
logout(){
this.props.history.push('/login')
}
render() {
return (
<div className="menu">
<NavLink to="/home/student">学生管理</NavLink>
<NavLink to="/home/setting">系统设置</NavLink>
<NavLink to="/home/user">个人中心</NavLink>
<button onClick={ ()=>this.logout() }>退出</button>
</div>
)
}
}
//使用withRouter组件包含当前组件
export default withRouter(Menu);
8.路由传参
(1)动态路由
第一步:创建一个详情页面
第二步:定义一个动态路由规则
exact 精确匹配,默认是模糊匹配,需要给具体的路由规则添加exact属性设置为精确匹配
<Route exact path="/home/student" component={ Student }></Route>
<Route path="/home/student/:sid" component={ StudentInfo }></Route>
第三步:在列表页面通过编程式导航跳转到详情页面
this.props.history.push('/home/student/'+id);
第四步:在详情页面获取动态路由参数
<p>学生编号:{ this.props.match.params.sid }</p>
(2)查询参数
如果路由传递的参数数量不固定时,使用动态路由就不合适了,可以使用查询参数的方式来进行数据的传递。
第一步:定义一个固定的路由规则
<Route path="/home/student/info" component={ StudentInfo }></Route>
第二步:在列表页面通过编程式导航进行页面跳转并传递参数
使用querystring插件
npm i querystring --save
使用querystring插件
import qs from 'querystring'
toInfo(obj){
// this.props.history.push('/home/student/'+id);
this.props.history.push({
pathname:'/home/student/info',
search:"?"+qs.stringify(obj)//qs.stringify把对象转换成字符串
})
}
第三步:在详情页面获取数据
var search = this.props.location.search.substr(1);//把参数中的问号去掉
// querystring parse 把 参数名=参数值&参数名N=参数值N 格式的数据转换成对象
var obj = qs.parse(search);//qs.parse把字符串转换成对象
二、状态管理
1.flux
React 标榜自己是 MVC 里面 V 的部分,那么 Flux 就相当于添加 M 和 C 的部分。
Flux 是 Facebook 使用的一套前端应用的架构模式。
流程:
view 触发action
action通知dispatcher(派发器)
dispatcher通知仓库修改状态
store仓库修改完状态后,通知页面进行重新渲染
安装:
npm i flux --save
第一步:创建仓库
/src/store/index.js
let state = {
name:'flux name',
age:18
}
export default { state }
第二步:在页面组件中引入仓库,并使用仓库中的数据
import React, { Component } from 'react'
import store from '../store'
export default class Setting extends Component {
render() {
return (
<div>
<h1>系统设置页面</h1>
<h3>仓库中的name为:{ store.state.name }</h3>
</div>
)
}
}
此时只能从仓库中获取数据
第三步:如果想通过页面发起一个事件,来修改仓库中的数据。
从flux中引出Dispatcher派发器,并注册好派发任务,用来修改数据
/src/store/index.js
import { Dispatcher } from 'flux'
let state = {
name:'flux name',
age:18
}
let dispatcher = new Dispatcher();
dispatcher.register(action=>{
switch(action.type){
case "changeName":
state.name = action.params
break;
default:
break;
}
})
export default { state,dispatcher }
action是一个形参,action.type是自定义的一个属性,用来判断要执行什么样的数据操作
action.params是自定义的一个属性,用来接收数据
此时可以在页面组件中通过dispatcher来派发一个具体的任务,用来修改数据
import React, { Component } from 'react'
import store from '../store'
import User from './User'
export default class Setting extends Component {
setName(){
//通过派发器来派发一个具体的任务,并告知操作的类型及数据
store.dispatcher.dispatch({
type:'changeName',
params:'hello,flux'
})
}
render() {
return (
<div>
<h1>系统设置页面</h1>
<h3>仓库中的name为:{ store.state.name }</h3>
<button onClick={ ()=>this.setName() }>修改</button>
<User />
</div>
)
}
}
此时,仓库中的数据会修改,但是页面中没有变化。
当仓库中的数据变化时,页面重新渲染
/src/store/index.js引入事件监听器
// 引入派发器
import { Dispatcher } from 'flux'
// 引入事件监听器(仓库数据变化后,页面也跟着重新渲染)
import EventEmitter from 'events'
class State extends EventEmitter{
name = 'flux name'
age = 18
}
let state = new State();
//实例化派发器
const dispatcher = new Dispatcher();
//注册具体的派发任务
dispatcher.register(action=>{
//根据传递的参数来执行不同的数据操作
switch(action.type){
case 'changeName':
state.name = action.params;
break;
case 'changeAge':
state.age = action.params;
break;
default:
break;
}
//触发一个事件
state.emit('change')
})
export default { state,dispatcher }
在页面组件的挂载完成钩子函数中用来监听仓库中的数据变化
import React, { Component } from 'react'
import store from '../store'
import User from './User'
export default class Setting extends Component {
setName(){
//通过派发器来派发一个具体的任务,并告知操作的类型及数据
store.dispatcher.dispatch({
type:'changeName',
params:'hello,flux'
})
}
componentDidMount(){
//页面组件挂载完成后
//通过仓库中的事件监听器进行监听,然后重新渲染页面
store.state.on('change',()=>{
this.setState({})
})
}
render() {
return (
<div>
<h1>系统设置页面</h1>
<h3>仓库中的name为:{ store.state.name }</h3>
<button onClick={ ()=>this.setName() }>修改</button>
<User />
</div>
)
}
}
2.redux
Redux由Dan Abramov在2015年创建的科技术语。是受2014年Facebook的Flux架构以及函数式编程语言Elm启发。很快,Redux因其简单易学体积小在短时间内成为最热门的前端架构。
Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树(对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers)。
redux核心概念:
actions
store
reducers
redux三个原则:
单一数据源
state是只读的
使用纯函数修改state
(1)安装
npm i redux
(2)基本语法
流程:
引入CreateStore
定义初始状态
定义纯函数
定义仓库
示例代码:
①引入createStore
/src/store/index.js
import { createStore } from 'redux'
②定义初始状态
const initalState = {
name:'小飞',
age:18
}
③定义纯函数
state 上一次修改完成后的状态
action是组件dipatch的动作
reducer一定要返回一个新的state,否则就检测不到state的变化
function reducer( state = initalState,action ){
switch(action.type){
case 'changeName':
return{
...state,
name:action.params
}
case 'changeAge':
return{
...state,
age:action.params
}
default:
return state;//一定要返回
}
}
④创建仓库
const store = createStore(reducer);
export default store;
⑤页面组件使用状态
引入仓库
import store from './store'
使用仓库中的数据
<p>仓库中的name为:{ store.getState().name }</p>
⑥改变状态
changeName(){
store.dispatch({
type:'changeName',
params:'小芳'
});
}
⑦使用订阅者实现数据变化页面就变化
componentDidMount(){
//仓库数据变化,想要重新渲染页面,就添加订阅者
store.subscribe(()=>{
this.setState({})
})
}