首先,redux
是基于Flux
思想的一种比较流行的状态管理工具,状态管理的本质:就是对应用程序的数据进行科学地流程化管理,目标是让数据变化可预期、可控(状态可以理解为 数据 ),而在开发项目中,它不是必须的。但如果开发大型项目应用,我们往往考虑的是如何高效的管理组件数据,那么redux
就是一种很不错的选择。就像 Dan Abramov(丹•阿布拉莫夫) 说的一样:
Flux 架构就像眼镜:您自会知道什么时候需要它。
其次,安装:
// 作用:创建store的
cnpm install redux -S
// 作用:把redux和react组件关联起来
cnpm install react-redux -S
OK,下面让我们来了解一下redux
的一些基本概念;
在我们使用脚手架搭建项目的时候默认是没有store
的,它是由我们手动在项目的src
目录下创建的一个保存数据的store
文件夹。redux
提供了createStore
这个官方API
用来生成store
。
首先,着重一下 store 的三大原则:
为此,在 store
文件夹的目录下,我们再创建一个 index.js
文件。
===============index.js===================
// 引入相应的api
import { createStore, combineReducers, applyMiddleware } from 'redux'
// 引入 子reducer
import testRreducer from './reducers/test'
// 引入 redux-thunk ,使得被 dispatch 的 function 会接收 dispatch 作为参数,并且可以异步调用它
import thunk from 'redux-thunk'
// 将多个子 reducer 合并成一个 reducer
let reducer = combineReducers({
test: testRreducer,
})
// 创建一个 store
let store = createStore(reducer, applyMiddleware(thunk))
// 导出
export default store
使用 store,在App.js主页面中引入store,并使用上下文进行关联,用共享数据包含需要用到共享数据的组件;
============== App.js ============
== 简述 ,后面有更完整的 App.js 代码 ==
import { Provider } from 'react-redux'
import store from '@/store'
// 在render生命周期中
render(){
return (
<Provider store={store}>
<Home></Home>
</Provider>
)
}
从上述代码中,我们看到了从 redux
中还引入了另外的两个 API
:combineReducers
、applyMiddleware
;
combineReducers
:随着应用数据变得越来越复杂,我们可以考虑将 reducer 函数拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。而 combineReducers
辅助函数的作用是把一个由多个不同 reducer
函数作为值的 object,合并成一个最终的 reducer
函数,然后就可以对这个 reducer 调用 createStore 方法。
applyMiddleware
:redux 默认只支持同步的 action,applyMiddleware 该 API 为 createStore 注入了 middleware(中间件),而我们引入的 redux-thunk 是用于基本 Redux 逻辑的推荐中间件,包括需要访问存储的复杂同步逻辑和像 AJAX 请求这样的简单异步逻辑。简单来说,就是我们引入 redux-thunk
作为中间件,使的被 dispatch
的 function
会接收 dispatch
作为参数,且可以异步调用它;在代码中我们可以简单的理解为 redux-thunk 把一个异步的 action 转化成三个同步的 action,来解决 redux 只支持同步的 action 的特点;
这三个同步的action分别是:
在运行之前不要忘记安装该中间件:
cnpm install redux-thunk -S
从目录结构以及代码中我们看到了reducer,那么reducer是做什么的呢?
简单来说reducer是一个纯函数,什么是纯函数?就是相同的输入必定有相同的输出,就叫做纯函数。reducer 作用就是用来改变 store 中的数据。定义reducer需要两个参数,分别是当前需要被共享的state以及用于改变state的action。
================reducers中的 test.js ==============
import { CHANGE_MSG} from '../actionTypes'
import { CNODE_LIST } from '../actionTypes'
// 定义初始数据
let initState = {
msg: 'hello redux',
cnodeList: []
}
// reducer 是概念之一,它是一个纯函数,用来改变数据的纯函数
// 这个纯函数有两个参数,参数1:被共享的数据,参数2:用来改变数据的action
export default function reducer(state=initState, action){
switch (action.type) {
case CHANGE_MSG:
console.log('它带着action来了', action)
// 将state进行深复制
let newState = JSON.parse(JSON.stringify(state))
newState.msg = action.payload
return newState
case CNODE_LIST:
return {...state, ...{cnodeList: action.payload}}
default:
return state
}
那么action又是什么呢?action我们可以理解为是一种改变数据的触发行为,作用就是通知reducer改变哪一条数据;
从目录结构中可以看到src目录下还有一个actionTypes的js文件,该文件的作用就是将actions文件夹中定义的关于action的文件进行统一管理;
==================actionTypes.js==============
// 把整个应用程序中所有的 action type 都写在这里
// 可以保证 action type 永远不会重复、冲突
export const CHANGE_MSG = 'CHANGE_MSG'
// 如果还有其他数据继续定义并导出
// 示例:
// cnode
export const CNODE_LIST = 'CNODE_LIST'
actions文件夹中定义的关于action的js文件中,有若干个数据就有若干个action的js文件
=============actions 中的 test.js==================
import { getCnodeList } from '@/utils/api'
import { CNODE_LIST } from '../actionTypes'
// 封装action,是为了代码复用
export function changeMsg(payload){
return {
type: 1,
payload
}
}
// 使用redux-thunk 将一个异步的action转化成三个同步的action
// 第一个action 的作用是告诉reducer有一个异步行为触发了
// 第二个action 的作用是告诉reducer异步行为成功了
// 第三个action 的作用是告诉reducer异步行为失败了
export function cnode(params){
return function(dispatch){
getCnodeList(params).then( res => {
// 第二个action,这是成功的action
dispatch({
type: CNODE_LIST,
payload: res
})
}).catch( err => {
// 第三个action,这是失败的action
dispatch({
type: CNODE_LIST,
payload: '失败了,'+ err
})
})
}
}
那么接下来就需要在“页面”组件中使用共享的数据了;
比如其中的一个Home页面组件:
=================Home.js===============
import React from 'react'
import { connect } from 'react-redux'
import { changeMsg, cnode } from '@/store/actions/test'
// 定义函数,将state中的数据变成当前组件的props
function mapStateToProps(state){
return {
msg: state.test.msg,
cnodeList: state.test.cnodeList
}
}
// 定义函数,将actions中的方法也赋给当前组件的props
function mapActionToProps(dispatch){
return {
xxx: function(payload){
return dispatch(changeMsg(payload))
},
getCnodeList: (params) => dispatch(cnode(params)) // 这里是发送第一个aciton,也就是告诉reducer有一个异步行为触发了
}
}
class Home extends React.Component{
// constructor(props){
// super(props)
// }
click(){
this.props.xxx('hello 12345')
}
componentDidMount(){ // 在redux可直接在componentDidMount生命周期中请求接口,且不和mobx一样,这里只会请求一次
let params = {
page: 1,
limit: 5,
tab: ''
}
this.props.getCnodeList(params)
}
createCnodeList(){
return this.props.cnodeList.map( ele => (
<div key={ele.id}>{ele.title}</div>
))
}
render(){
return (
<div>
<h2>首页</h2>
<h1>{this.props.msg}</h1>
<button onClick={this.click.bind(this)}>修改msg</button>
<hr/>
<div>
{this.createCnodeList()}
</div>
</div>
)
}
}
export default connect(mapStateToProps, mapActionToProps)(Home)
在 views 目录下的统一出口文件 index.js
================= index.js =====================
import loadable from '@loadable/component'
// 路由懒加载模式
const Home = loadable( () => import('./home/Home'))
export default [
{
id: 101,
path: '/',
component: Home,
text: '首页'
}
]
主页面组件App.js
============== App.js ===================
import React from 'react';
// 引入路由
import { HashRouter, NavLink, Route, Switch, Redirect } from 'react-router-dom'
// 引入侧边导航的数据
import routes from '@/views/'
// redux相关
import { Provider } from 'react-redux'
import store from '@/store/'
export default class App1 extends React.Component {
constructor(props){
super(props)
this.state = {
};
}
// 创建侧边导航
createNavLink(){
return routes.map( ele => (
<div key={ele.id}>
<NavLink to={ele.path}>{ele.text}</NavLink>
</div>
))
}
// 创建content
createConentList(){
let arr = []
routes.map( ele => {
arr.push(
<Route
key={ele.id}
path={ele.path}
component={ele.component}
exact
>
</Route>
)
return false
})
return arr
}
render(){
return (
<HashRouter>
<Provider store={store}>
<div>
{this.createNavLink()}
</div>
<div>
<Switch>
{this.createConentList()}
</Switch>
<Redirect from='/*' to='/good'></Redirect>
</div>
</Provider>
</HashRouter>
)
}
}
最后,让我们来捋捋 redux
的流程;
store 中的数据通过 reducer
来修改,那么需要定义 reducer,定义 reducer 需要两个参数,参数 1 为当前需要被共享的数据,参数 2 为用于改变数据的 action,从而引出 action,action 就是触发改变 store 数据的一种行为或者说是信号,在 action
中定义一个改变数据的行为(函数),该函数中 return 一个 action 对象,该对象也就是定义 reducer 中的参数2 action
,接着在 reducer 中一般通过 switch 语句判断 action 对象的 type 属性来改变数据,然后在需要用到共享数据的组件中用 Provider 组件使用 store 包裹,那么触发了行为(action 中定义的函数),与 reducer 中的参数 2 action
的 type (信号)相对应、相匹配,进而改变视图。
store 的三大原则: