redux简述
先来讲讲redux是什么吧,我们通常在react开发中都会这么说说,引入redux处理数据流,但是这里讲的redux已经是经过二次封装的内容react-redux了,而他的原始形态才是redux,是一个不依赖react就能进行数据流处理的组件。而redux的又是脱胎于flux思想。flux是由facebook工程师提出的一种解决方案,他的名字是拉丁语的flow,主要是为了解决MVC架构存在的问题。他的核心思想是数据和逻辑永远单向进行流动。 讲到这里,我们先描述下flux当时想要解决的问题(笑,也就是大名鼎鼎的MVC结构。
MVC结构下代码的书写方式
首先,我们先考虑下传统的react的数据处理的组件结构来实现一个博客的论坛评论app 论坛评论的app主要包含两个功能:
- 评论展示区
- 评论编辑区
我们尝试着去书写一个评论展示区的方案,专门用一个model组件来处理数据,再使用一个纯函数组件来进行业务逻辑组件的展示 代码如下:
import React,{Component,PropTypes}from 'react';
class CommentListContainer extends Component{
constructor(props){
super(props)
this.state={loading:true,error:'',value:''}
}
componentDidMount(){
this.props.promise.then(res=>res.json())
.then(value=>this.setState({loading:false,value}))
.catch(error=>this.setState({loading:false,error}))
}
render(){
const{loading,error,value}=this.state
if(loading){
return <span>loading....</span>
} else if(error.length!==0){
return <span>Error:{error}</span>
}else{
return(
<CommentList comments={list}
)
}
}
}
function CommentList({comments}){
return(
<ul>
{
comments.map((v,i)=>(
<li key={v.id}>{v.text}</li>
))
}
</ul>
)
}
以上是我们在不使用redux的时候进行的react组件方案
这样子看起来是和数据解耦了,但是实际上数据还是在对应的组件内部进行保存,并不算彻底的解耦
而以上的模型就是我们我们在MVC模型中讲述的Model和View,至于为什么没有Controller,因为对于纯函数组件来说,他不需要感知他做了什么内容,只需要知道用户的操作需要激发一个更改,所以如果有数据操作,也会放在Model中进行代码的书写,而View仅仅进行一个操作的动作 MVC Model负责同步数据,校验数据 View负责可视化,Controller负责连接View和Controller, MVC是上世纪80年代被提出的概念,直到2005年,他的问题被放大 那么MVC的问题是什么呢,下面用一张流程图来展示
这是一张我在processon上绘制的流程图,因为model的set和get都是暴露在外的,所以view可以随意更改model的数值,一个view往往只会更改一个model,但是一个model更改了可能多个view的数值都被同时更改,而且model之间也能随意更改对方的Flux的解决方案
讲完了mvc的解决方案来讲讲flux的解决方案,用一张图表示刚刚说的核心思想,数据和逻辑永远单向流动
数据保证从action->dispather->store->view->action这样一个循环 这种渲染其实是一种全面的渲染,但是因为使用了virtual DOM,并不会过多的影响性能,而且通过pureRender等方式保障节点的局部渲染。 但是flux并不是万能的,从复杂场景来说,比如浏览器的控制台,flux流程能比较好的降低复杂度redux在实际中的运用
运用方式想必大家都很熟悉了,通过connect连接store,dispatch分发action来触发的机制 这里注重给大家介绍几种比较实用的中间件
- redux-form-utils 这个中间件的目的是为了减少创建表单的冗余代码 大家可以想象下原生react处理表单,下面只展示核心代码
handleChangeName(e){
this.setState({
name:e.target.value
})
}
handleChangeAddress(e){
this.setState({
address:e.target.value
})
}
render(){
const {name,address}=this.state
return(
<form>
<input name="name" value={name} onChange={this.handleChangeName} />
<input name="address" value={address} onChange={this.handleChangeAddress} />
</form>
)
}
可以看得出来,这个change事件的代码非常容易,而且大部分处理逻辑类似,这个时候用中间件可以怎么写
import {createForm } from 'redux-form-utils'
@createForm({
form:'my-form',
fields:['name','address']
})
class Form extends Component{
render(){
const {name,address}=this.props.fields;
return(
<form className='form'>
<Input name='name' {...name} />
<Input name='address' {...address} />
</form>
)
}
}
通过deractor+高阶组件的写法,内部封装了有关于form表单的处理来实现对代码的简写 用过antd的同学肯定发现了,这种写法其实就是antd的form表单的写法. redux-form-utils除了createForm还有个bindRedux,具体作用是在如果表单的数据是用来整个存放在redux中的表单数据
- redux-thunk 主要是以多参数的形式currying实现对函数的惰性求值。可以用来改造同步的dispatch为异步 核心代码如下:
function createThunkMiddleware(extraArgument){
return({dispatch,getState})=>next=>action{
if(typeof action==='function'){
return action(dispatch,getstate,extraArgument)
}
return next(action)
}
}
当action为函数的时候,这个行为就被拦截了,而不是派发到reducer中去找到对应的action触发内容,这里的action就是一个thunk函数,将dispatch和getState派发到函数中 初始化thunk函数方法如下:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(
reducer,
applyMiddleware(thunk)
);
具体调用中可以这么写
let apis=(url,params)=>{
return(dispatch,getState)=>{
fetch(url,params).then(result=>{
dispatch({
type:'GET_WEATHER_SUCCESS',
payload:result
});
}).catch(e=>{
dispatch({
type:'GET_WEATHER_ERROR',
error:error
})
})
}
}
store.dispatch(apis('https://xx.xx.xx.xx',{a:1,b:2}))
后续再补充别的中间件