两周已过,小编携文来扰!!javascript
最近小编委托组内小哥又给本身梳理了一遍react
结合redux
使用的知识点(由于懒,翻文档不如白嫖来的开心呀),主要涉及使用的注意事项和使用流程,涉及的中间件以及如何处理异步数据等。完后,小编以为有必要对此次的知识点作一个系统的整理,造(wu)福(ren)大(zi)众(di)。html
文中小编对于一些涉及流程的模块针对性的画了流程图,同时穿插了代码,方便理解(毕竟全是文字会显得文章很干)。若触到只是盲区想细品,小编建议从上往下看;若以为自身掌握的能够,可直接跳至文尾,有惊喜!话很少说,安排起来!前端
首先,咱们须要知道的是:
React
是一个声明式的,高效且灵活的用于构建用户界面的javascript
库。React
能够将一些简短,独立的代码片断(亦称‘组件’)组合成复杂的UI
界面。vue
注意: react
是一个库,而不是框架。java
关于库: 库(Lab
)是将代码集合成的一个产品,以供研发人员调用。库为咱们提供了不少封装好的函数,咱们在使用时只须要提取本身须要的函数便可,使用起来也很是灵活。如果没有,咱们也能够手动封装函数实现。像jQuery
、react
、underscore
就是库。node
关于框架: 框架(Framework
)则是为解决一个(一类)问题而开发的产品。通常状况下,框架用户只须要使用框架提供的类或函数,便可实现所有功能。像angular
、backbone
、vue
等这些属于框架。react
举个例子: 就好比你买了一辆小摩托,小摩托买回来就能够用了,这里小摩托就至关于一个框架。而后某天你骑着心爱的小摩托去溜达,发现有人跟你骑着同样的小摩托,你想让本身的小摩托变得跟别人不同,就给本身小摩托换个外形或者某个好看的配件。这里换的配件就至关于库。web
事实上,库的使用是很是灵活的,可是没有框架来的方便,小编认为这是二者间的主要区别。此外,框架自己是有一套属于本身的解决方案的,可是react
身为库的同时,其自己最大的做用就是用来写UI
组件,自身并无具有异步处理机制,模块化以及表单验证等,主要充当一个前端渲染的库而已,只有将React
和react-router
,react-redux
,redux-saga
等结合起来使用才称得上框架。redux
上面已经提到,react
主要纯粹是用来写UI
组件的,它能够与任何web
程序一块儿使用。其中,最为常见的是使用react.js
进行单页面程序(SPA
)的开发。segmentfault
virtual Dom
,大大提高了渲染性能;virtual Dom
解问决了跨浏览器问题,并提供了标准化的API
;上面也讲到,react
只是一个纯粹写UI
组件的库,并非一个框架。在项目开发中,仅仅是使用react
明显是不够的(首先数据处理部分就很麻烦),此时须要结合react-router
,react-redux
,redux-saga
等(或者是ReactRouter
和Flux
)使用,才能开发一个项目。
react-router
简单来说就是经过URL
为react
页面导航的路由,它经过管理 URL
,实现组件的切换和状态的变化。react-router
的核心概念是Router
和Route
。
在这里,咱们须要明白的一点是,Router
在这里做为一个容器,用于包裹Route
。具体的路由跳转是由Route
实现的。
举个栗子:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";
ReactDOM.render(
<Router>
<Route path="/" component={App} />
<Route path="/user" component={User} />
...
</Router>,
node
);
复制代码
说明:在这里引入了react-router-dom
。其实react-router
和react-router-dom
的主要区别在于后者比前者多了<Link>
, <BrowserRouter>
这样的DOM
类组件,其余没啥区别,引入时只需引入一个便可。固然,若是要搭配redux
,还须要引入react-router-redux
。
Router
做为包裹Route
的容器,当访问跟路由/
时,组件APP
就会加载到document.getElementById('app')
。当访问路由/user
时,将会呈现由组件User
渲染构建的UI
页面。
react-router
最大的做用主要仍是为react页面的实现路由跳转鞠躬尽瘁,保驾护航。
在讲react-router
的使用以前,想简单的讲解一下router
的history
。
Router
的history
有三种类型:
HashHistory
和HashRouter
BrowerHistory
和BrowerRouter
reateMemoryHistory
和MemoryRouter
对于以上三种history
,官方上看到推荐使用BrowerHistory
。至于缘由,多是由于使用browserHistory
时,url
格式更加好看吧(小编瞎说的)。不过browserHistory
使用下,浏览器表现的url
的格式更加符合通常浏览器url
的格式。例子以下:
使用HashHistory
,浏览器的url
是这样的:/#/user/add?page=1
;
使用BrowserHistory
,浏览器的url
是这样的:/user/add
;
相比之下,使用BrowserHistory
,url
表现的形式可能更能被接受。可是须要注意,它须要server
端支持。使用HashHistory
的话,由于带有#
的缘故,浏览器不会去发送request
,react-router
会本身根据路由去渲染相应的模块(适用于静态页面)。
关于react-router
的使用,没有什么比官方文档讲解的更全面的吧。
附上连接:reacttraining.com/react-route…
不想看官网,能够,阮一峰老师讲解的也很不错呀。
连接:www.ruanyifeng.com/blog/2016/0…
不过阮老师写的这个只适合react-router 2.0
版本的,童鞋们看的时候稍微注意一下。
咱们知道,react
的数据流是自顶向下的单项数据流,数据间的传递是经过父组件传递给子组件这一方式传递的。父组件的state
能够做为子组件的props
来传递数据,当state
改变时,props
也随之改变,可是props
自己是不可改变的。
在项目当中,不一样层级的页面之间每每须要传递数据。当须要传值的页面数量变多的状况下,传值关系可能会发生混乱。这时候要是有一个容器,可以帮助管理react
的state
的状态,那就很nice
。接下来就是redux
登场的时刻了!
redux
是javascript
状态容器,提供可预测化的状态管理。它能够构建一致化的应用,运用于不一样的环境(客户端、服务器端、原生应用),而且易于测试。
1.Action
action
是惟一能够改变状态的途径,同时它也是store
的惟一数据来源。通常状况下,经过dispatch
触发相应的action
,从而达到改变store
中state
的目的。(注意,action
是一个对象)
举个例子:
const action = {
type: 'xxx/add', // xxx是namespace名,type属性为必须
payload: {name: 'phoebe'},
... //可根据需求写callback()回调函数
}
复制代码
2.Reducer
reducer
,简单的说就是一个函数,它接受由dispatch
触发的action
和当前的state
做为一个参数,返回一个新的state
(简单的说就是根据action
来更新state
)。
举个例子:
const Reducer = ({state, action}) => {
...
return newState; //返回的新的state
}
复制代码
3.Store
Store
是把action
和reducer
联系起来的一个对象。Store
能够理解为一个存储数据的仓库,管理着整个应用的状态。
注意: Redux 应用只有一个单一的 store。
Store的职责:
- 维持应用的
state
;- 提供
getState()
方法获取state
;- 提供
dispatch(action)
方法更新state
;- 经过
subscribe(listener)
注册监听器;- 经过
subscribe(listener)
返回的函数注销监听器。
Redux
经过 createStore
这个函数,来生成store
对象:
import { createStore } from 'redux';
import todoApp from './reducers';
let store = createStore(todoApp)
复制代码
同时,想获取到当前的state
时,能够经过getState()
这个方法来获取:
const state = store.getState()
复制代码
小编给他们之间的关系画了个图,以下:
当项目较为简单,没有过多的交互,View
只从单一来源获取数据或不须要与服务器大量交互(或不使用websocket
)时,能够不使用redux
(使用了可能必定程度上会使项目变得更复杂)。
可是如下几种状况可使用redux
:
WebSocket
View
要从多个来源获取数据从组件的角度看,如下几种状况可使用redux
:
拓展:若是对为何react
要使用redux
还有不了解的童鞋,能够去看看下面连接的内容,小编以为看完确定就明了了。
连接: segmentfault.com/a/119000001…
react-redux
是redux
的官方react
的绑定库。它可以使你的react
组件在redux
的store
中读取数据,并向store
分发actions
以便更新数据。
react-redux
将全部的组件分为两大类:分别是UI
组件(presentational component
,又称傻瓜组件/无状态组件)和容器组件(container component
)。
UI
组件具备如下几个特征:
UI
呈现,不带有任何的业务逻辑UI
的渲染只能经过外部传入props
来改变(也就是不使用this.state
)this.props
)对象提供redux
的API
简单的来讲,UI
组件就是负责页面的渲染。
举个例子:
const page = number => <p>this is {number} page </p>
复制代码
容器组件和UI
组件在必定程度上偏偏相反:
redux
的API
简单来讲,容器组件就是负责管理数据以及处理页面的业务逻辑。
注意: 如果一个组件内既有UI
组件又有逻辑,能够考虑将其拆分红外面是一个容器组件,里面包含一个UI组件的结构。前者负责与外部通讯,将数据传给后者,后者负责页面的渲染。
React-Redux
规定,全部的 UI
组件都由用户提供,容器组件则是由 React-Redux
自动生成。也就是说,用户负责视觉层,状态管理则是所有交给它。
react-redux
提供了两个重要的API
:connect
和Provider
。
react-redux
提供了<Provider>
组件,用于链接Store
,把store
提供给内部组件,内部组件接受store
做为props
,而后经过context
往下传,这样react
中任何组件均可以经过context
获取store
(简单来讲就是使得咱们的整个app
都能访问到redux store
中的数据)。
举个例子:
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from './store';
import App from './App';
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App /> // Provider的子组件能够拿到store状态
</Provider>,
rootElement
);
复制代码
同时,React-Redux
提供一个connect
方法,让咱们能够把组件和store
链接起来。
import { connect } from "react-redux";
import { increment, decrement, reset } from "./actionCreators";
// const Counter = ...
const mapStateToProps = (state /*, ownProps*/) => {
return {
counter: state.counter
};
};
const mapDispatchToProps = { increment, decrement, reset };
export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
复制代码
这样,咱们就能从store
中获取相应的数据到Counter
中。
connect
的做用就是将UI
组件和容器组件连接起来,本质的做用其实就是充当一个链接器。
connect()
接受四个参数,可是通常状况下最常使用的是前两种:
connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {})(component)
复制代码
1.mapStateToProps
做为connect
的第一个参数,mapStateToProps
用来从store
中选择被链接的组件所须要的数据。
注意:
store
的state
改变时,就会被调用store
的state
,而且返回组件所须要的数据举个例子:
import React from 'react';
import { connect } from 'dva';
const Alarm = ({permission, ....}) => { // 定义的函数组件
//...
};
const mapStateToProps = ({ app }) => { //从store中摘出app
const { permission } = app; //从app中摘出组件须要的权限
return {
permission,
};
};
export default connect(mapStateToProps)(Alarm);
复制代码
2.mapDispatchToProps
做为第二个传入connect
的参数,mapDispatchToProps
能够实现向store
中分发acions
。这也是惟一触发一个state
变化的途径。它是用来创建UI
组件的参数到store.dispatch
方法的映射,能够是一个函数,也能够是一个对象。
React-Redux
提供了两种能够分发actions
的方式:
props.dispatch
而后本身分发actions
。connect
可以接收一个mapDispatchToProps
做为第二个参数,这可让咱们可以建立dispatch
调用方法,而后把这些方法做为props
传递给咱们的组件。当咱们不把mapDispatchToProps
做为connect
的第二个参数传入时,看下官方例子:
connect()(MyComponent);
// 与下面语句等价
connect(
null,
null
)(MyComponent);
// 或者
connect(mapStateToProps /** 没有第二个参数 */)(MyComponent);
复制代码
如果咱们使用这种方式,咱们的组件就会接收props.dispatch
,它能够用来分发组件中的actions
。
看个更详细的例子:
import React from 'react';
import { connect } from 'dva';
const Alarm = ({dispatch, permission, ....}) => { // Alarm组件接收props的dispatch
const onAdd = () => {
dispatch({ //dispatch 用于触发onAdd方法的action
type: 'xxx',
payload: {...}
})
}
return(
<div onClick={onAdd}> //添加触发事件
//...
<div>
)
};
const mapStateToProps = ({ app }) => {
const { permission } = app;
return {
permission,
};
};
export default connect(mapStateToProps)(Alarm);
复制代码
小结:有关mapDispatchToProps
部分,小编主要结合所作项目作了相应的总结,关于它的函数形式和对象形式,有兴趣的童鞋能够点击此处了解详情。
3.mergeProps
mergeProps
的格式为: mergeProps(stateProps, dispatchProps, ownProps)
。
mergeProps
是connect
的第三个参数,可选。它将mapStateToProps()
与mapDispatchToProps()
返回的对象结果和组件自身的props
合并成新的props
,而后传入组件。默认返回Object.assign({}, ownProps, stateProps, dispatchProps)
的结果。
写成例子以下:
const mergeProps = () => {
return Object.assign({}, ownProps, stateProps, dispatchProps)
}
复制代码
4.options
做为connect
的第四个参数,经过配置项能够更加详细的定义connect
的行为,通常状况下只须要执行默认值。option
有不少,举个官方例子:
{
context?: Object,
pure?: boolean,
areStatesEqual?: Function,
areOwnPropsEqual?: Function,
areStatePropsEqual?: Function,
areMergedPropsEqual?: Function,
forwardRef?: boolean,
}
复制代码
小结:关于react-redux
, 咱们须要重点掌握它提供的两个组件--provider
和connect
,它们的用法。同时对于其它相关概念也须要了解一下。
redux-saga
是一个用于管理应用程序Side Effect
(反作用,例如异步获取数据,访问浏览器缓存等)的Library
,它的目标是让反作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。
能够简单的理解为,redux-saga
在redux
中扮演着‘中间件’的角色,主要做用是用来执行redux
中数据的异步操做。在执行异步操做时,须要借助ES6
中的generator
函数和yield
关键字来以同步的方式实现异步操做。(它的功能有点像redux-thunk+async/await
,经过建立 Sagas
将全部的异步操做逻辑都存放在一个地方进行集中处理)
由于redux
中的action
须要redux-thunk
或者redux-saga
这样的‘中间件’去作异步处理。(就一句话,简洁明了吧)
流程图以下:
简单来讲就是:ui
组件触发action
建立函数 -> action
建立函数返回一个action
-> action
被传入redux
中间件(被 saga
等中间件处理) ,产生新的action
,传入reducer
-> reducer
把数据传给ui
组件显示 ->mapStateToProps
-> ui
组件显示
call
异步阻塞调用put
至关于dispatch
,分发一个action
select
至关于getState
,用于从store
中获取响应的state
fork
异步非阻塞调用,无阻塞的执行fn
,执行fn
时,不会暂停Generator
tak
e监听action
,暂停Generator
,匹配的action
被发起时,恢复执行。take
结合fork
,能够实现takeEvery
和takeLatest
的效果takeEvery
监听监听action
,每监听到一个action
,就执行一次操做takeLatest
监听action
,监听到多个action
,只执行最近的一次cancel
指示middleware
取消以前的fork
任务,cancel
是一个无阻塞的Effect
。也就是说,Generator
将在取消异常被抛出后当即恢复race
竞速执行多个任务throttle
节流优势
action
是个普通对象,与redux
的action
保持一致Effect
),方便异步接口的测试worker
和watcher
能够实现非阻塞异步调用,同时能够实现非阻塞调用下的事件监听缺点:相对于新手来讲,学习难度有点大,成本有点高(如果不考虑学习成本,建议用redux-saga
)
干讲可能有些童鞋会迷惑,下面小编举个代码例子讲一下redux-saga
在代码中具体是怎样异步处理数据的。
注意,如下是小编从demo
中抽取的一个页面代码,为了方便理解,全部组件都包含在<APP></APP>
中,APP
做为父组件,它 的state
将做为全部子组件的props
(能理解吧?)。
// magagement.js
import React from 'react';
import { connect } from 'dva';
import { Card, Select } from 'antd';
import Page from 'components/Page';
import Search from 'components/Search';
const Management = ({dispatch, management, permission }) = {
addClick = () => {
dispatch({ //当点击按钮时,触发action,就是文中所说的‘点击UI组件触发action’
type: 'management/add', // 触发以后将action中的type和当前payload传到reducer
payload: { name: phoebe },
});
};
return (
<Page title="xxx">
<Card>
<Search
extra={
permission.includes('xxx/xxxx') && (
<Button type="primary" icon="plus" onClick={addClick}> //为按钮添加一个触发事件
一个小可爱呀
</Button>
)
}
/>
</Card>
</Page>
)
};
const mapStateToProps = ({ app, management }) => { // 从store中抽出Managenent须要的数据
const { permission } = app;
return {
management,
permission,
};
};
export default connect(mapStateToProps)(Management); // 利用connect,将抽出的数据做为子组件的props传入
// Management.js的Models
import modelExtend from 'dva-model-extend';
import { pageModel } from 'models/common';
import { addManagement } from 'services/firmware'; // 接口
export default modelExtend(pageModel, {
namespace: 'management',
state: {},
subscriptions: {
setup({ dispatch, history }) {
history.listen(location => {
if (location.pathname === 'xxx/managenent') {
//...
}
})
}
},
effects: { //redux-saga管理反作用(effect),具体做用体如今这儿
//...
*add({ payload }, { call, put }) {
const data = yield call(xxx, payload); // 将处理的数据上传接口,以后UI更新显示
yield put({ // 建立并 yield 一个 dispatch Effect
type: 'updateState',
payload: {
name: 'Tins',
},
});
//...
},
},
});
//Management的路由
import React from 'react';
import { routerRedux, Route, Switch } from 'dva/router';
import App from 'routes/app';
import { LocaleProvider, Spin } from 'antd';
import zhCN from 'antd/lib/locale-provider/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('zh-cn');
const { ConnectedRouter } = routerRedux;
function RouterConfig({ history, app }) {
//....
const Management = dynamic({
app,
component: () => import('./xxx'),
});
return (
<ConnectedRouter history={history}>
<LocaleProvider locale={zhCN}>
<App>
<Switch>
//...
<Route path="/xxx" exact component={Management} /> // Mamagement做为APP的子组件,APP的store state将做为Management的props。
</Switch>
</App>
</LocaleProvider>
</ConnectedRouter>
);
}
复制代码
小结: 关于redux-saga
,使用的大体流程就是这样,以为有不明白或者小编有描述不清晰的请留言。更加详细的知识点能够去redux-saga
的官网瞅瞅。
redux-thunk
也是redux
的一个中间件(middleware
)。当dispatch
一个action
以后,到达reducer
以前,进行一些额外的操做时(处理action
反作用),就须要使用到redux-thunk
。它的做用跟redux-saga
相似,都是用来处理异步数据。
有关redux-thunk
,跟saga
相比,其实小编以为并无说哪一个更好,或者哪一个很差,主要是看我的更加擅长使用哪一个吧。在这里小编就很少说了,有关这部分的知识点,小编建议能够去看看阮一峰老师写的文档。
为了省去文字,更加清晰的将文中所述的知识点链接起来,小编尝试画了个图,供理解。以下:
(如有疏漏,请留言指,手动笔芯~)
关于react
结合redux
以及react-router(react-router-dom)
,redux-saga
等,小编就总结到这儿吧。划重点:主要了解他们之间的联系并懂的使用。
唠嗑一下,最近小编发现,有的童鞋关注了,收藏了,可是就轻飘飘的溜了,这是咋回事儿?!
仍是内句老话,如果发现小编哪儿梳理的有问题,欢迎下方留言,小编瞅到必定及时更正。若觉尚可,嘿嘿(整理不易,小编也须要鼓励)。