先看看几个异步处理方式:
callback:传入一个回调函数,在未来某个不确定的时间点调用,想要控制异步的调用顺序,需要加入很多变量和代码逻辑,引发‘回调地狱’。
thunk:是一种你无需你传入任何参数就可以正常工作的函数,如果你想获取其中的值,则需要传入一个回调函数。thunk 把时间的概念抽象出去的,执行 thunk 函数后,我们只需要等待结果就行,无需去关心内部是什么,做了什么事情,需要花费多少时间。thunk 的本质其实是使用闭包来管理状态。
Promise:核心在于其通过一种协议[4]保障了then后注册的函数只会被执行一次。这和上面提到的回调不同,普通的 callback 实际上是第三方直接调用我们的函数,这个第三方不一定是完全可信的,我们的回调函数可能会被调用,也可能不会调用,还可能会调用多次。
缺点:一旦开始执行就没办法手动终止;
我们无法完全捕获可能的错误。比如说 .catch 中的错误就难以再被捕获;
Generator:把我们的代码分割成了独立可阻塞的部分,局部的阻塞不会导致全局的阻塞,需要我们手动调用 .next()来继续执行后面的内容。我们完全不知道谁会在什么地方调用.next(),结合 Promise 我们可以比较轻松的解决「控制反转」的问题,一些人把 Promise + Generator 当作是异步最好的解决方案之一。
async await:和generator类似,async 函数只有在结束时,才会返回的是一个 Promise。我们无法控制其中间状态,而 generator 返回的是迭代器,迭代器让你有充分的控制权。
Redux 设计和使用的三大原则:
1. 单一数据源:
一个应用永远只有唯一的数据源。使用单一数据源的好处在于整个应用状态都保存在一个对象中,这样我们随时可以 6提取出整个应用的状态进行持久化(比如实现一个针对整个应用的即时保存功能)。此外,这样 的设计也为服务端渲染提供了可能。工具函数combineReducers 可解决该数据源对象过于庞大的问题
combineReducers 的返回 值是一个归一 化的 rootReducer 函数 。 也就是说, combineReducers作为一个函数,它返回了另一个函数( rootReducer)。“函数返回一个新的函数”,这就是函数式编程的典型思想。 combineReducers 返回的函数就是 一个标准的 reducer,它的参数接收 state 和action
const combineReducers = (reducers) => {
return (state={} , action) => {
return Object.keys(reducers) .reduce(
(nextState , key) =>
nextState [key] = reducers [key] (
state [key] ,
action
);
return nextState;
},
{}
);
}
}
2. 状态是只读的:
定义一个 reducer, 它的功能是根据当前触发的 action 对当前应用的状态(state)进行迭代,这里我们并没有直接修改应用的状态,而是返回了一份全新的状态。Redux 提供的 createStore 方法会根据 reducer 生成 store。最后,我们可以利用 store. dispatch方法来达到修改状态的目的。
3. 状态修改均由纯函数完成
Redux 利用每次新返回的状态生成酷炫的时间旅行(time travel)调试方式,让跟踪每一次因为触 发 action 而改变状态的结果成为了可能。
通过 createStore 方法创建的 store 是一个对象,包含以下方法。
getState():获取 store 中当前的状态。
dispatch(action):分发一个 action,并返回这个 action,这是唯一能改变 store 中数据的方式。 subscribe(listener):注册一个监听者,它在 store 发生变化时被调用。
<Provider/> 接受一个 store 作为props,它是整个 Redux 应用的顶层组件,而 connect() 提供了在整个 React 应用的任意组件中获取 store 中数据的功能。
redux-middleware:
import compose from './compose';
let newStore = applyMiddleware(mid1, mid2, mid3, ...)(createStore)(reducer, null);//创建一个普通的 store
export default function applyMiddleware(...middlewares) {//applyMiddleware 的结构也是一个多层 currying 的函数。借助 compose,applyMiddleware 可以用来和其他插件加强 createStore 函数:
return (next) => (reducer, initialState) => {
let store = next(reducer, initialState);//利用 createStore 和 reducer 创建了一个 store
let dispatch = store.dispatch;
let chain = [];var middlewareAPI = {
getState: store.getState, dispatch: (action) => dispatch(action),};//store 的 getState方法和 dispatch 方法又分别被直接和间接地赋值给 middlewareAPI 变量 store
chain = middlewares.map(middleware => middleware(middlewareAPI));//middleware 带着 middlewareAPI 这个参数分别执行一遍。执行完后,获得 chain数组,保存的对象是第二个箭头函数返回的匿名函数dispatch = compose(...chain)(store.dispatch);//将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch。当新 dispatch 执行时,数组内middleware从右到左依次执行
return { ...store,
dispatch,};
}
}
logger middleware 的实现:
export default store => next => action => {//applyMiddleware 会对 logger 这 个 middleware 进行层层调用,动态地将 store 和 next 参数赋值。
console.log('dispatch:', action);
typeof action === 'function' ? action(store.dispatch, store.getState) : next(action)//判断 action 是否是函数。如果是,则执行 action,否则继续传递 action 到下 一个 middleware。
console.log('finish:', action);
}
redux-saga: 采用generator,代替了async、await、promise等
react-router-redux:
对于前端应用来说,路由状态(当前切换到了哪个页面,当前页面的参数 有哪些,等等)也是应用状态的一部分。在很多情况下,我们的业务逻辑与路由状态有很强的关 联关系。比如,最常见的一个列表页中,分页参数、排序参数可能都会在路由中体现,而这些参 数的改变必然导致列表中的数据发生变化。因此,当我们采用 Redux 架构时,所有的应用状态必须放在一个单一的 store 中管理,路由 状态也不例外。而这就是 React Router Redux 为我们实现的主要功能。
对 Redux 的 store 进行一些增强,以便分发的 action 能被正确识别
import { browserHistory } from 'react-router';
import { routerMiddleware } from 'react-router-redux';
const middleware = routerMiddleware(browserHistory);
const store = createStore(
reducers,
applyMiddleware(middleware)
);
可以用 store.dispatch 来分发一个路由变动的 action 了:
import { push } from 'react-router-redux';
// 切换路由到 /home store.dispatch(push('/home'));
高阶 reducer 主要通过下面 3 点来增强reducer:高阶 reducer 就是指将 reducer 作为参数或者返回值的函数
能够处理额外的 action;
能够维护更多的 state; 将不能处理的 action 委托给原始 reducer 处理。createStore 的函数签名:
export default function createStore(reducer, initialState, enhancer) { // ...}
createStore 中的第二个参数不仅扮演着 initialState 的角色。如 果我们传入的第二个参数是函数类型,那么 createStore 会认为你忽略了 initialState 而传入了 一个 enhancer。
如果我们传入了一个有效的enhancer,createStore会返回enhancer(createStore)(reducer, initialState) 的调用结果,这是常见的高阶函数调用方法。在这个调用中,enhancer 接受createStore 作为参数,
对 createStore 的能力进行增强,并返回增强后的 createStore。然后再 将 reducer 和 initialState 作为参数传给增强后的 createStore,最终得到生成的 store。
var currentReducer = reducer
var currentState = initialState
var listeners = []
var isDispatching = falsefunction getState() {
return currentState//getState 方法用于返回当前状态。
}
const dispatch = (action) => {
state = currentReducer(state, action );
listeners . forEach(listener =>listener( ));
}
const subscribe = (listener) => {
listeners .push(listener);
//返回一个函数 , 进行取消 订 阅
return () => listeners = listeners filter (item =>工 tern!== listener) ;
}
return { getState , dispatch , subscribe };
dispatch---将当前的状态和 action 传给当前的reducer,用于生成最新的 state。在得到新的状态后,依次调用所有的监听器,通知状态的变更。
需要注意的是,我们在通知监听器变更发生时,并没有将最新的状态作为参数传递给这些监听器。这是因为在监听器中,我们可以直接调用 store.getState() 方法拿到最新的状态。
React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。
React-Redux 提供connect
方法,用于从 UI 组件生成容器组件。connect
的意思,就是将这两种组件连起来。
connect 函数本身返回名为 wrapWithConnect 的函数,而这个函数才是真正用来 装饰 React 组件的。而在我们装饰一个 React 组件时,
其实就是把组件在 Connect 类的 render 方 法中进行渲染,并获取 connect 中传入的各种额外数据。
connect 函数如果传入mapStateToProps,则会store.subscribe()监听state变化
connect ( mapStateToProps ,mapDispatchToProps,mergeProps,options)
在 options 中,支持自定义 areStatesEqual 函数,以便进行前后两次 state 比较。 areStatesEqual函数接收两个参数,第一个是前一个 state,第二个是后一个 state。 如果该函数的返回值为 true,那么 mapStateToProps 方法便不再执行,因为规避了不必要的渲染 。
Provider
在根组件外面包了一层,这样一来,App
的所有子组件就默认都可以拿到state
了。它的原理是React
组件的属性
class Provider extends Component {
getChildContext() {
return {store: this.props.store}
}
render() {
return this.props.children;
}
}
子组件就可以从context
拿到store
createSelector:
export default createSelector(
allitemsSelector ,
selectedidSelector ,
getItems)
createSelector函数可以接收多个参数,最后一个参数要保证为计算最终 state片段的规则函数,前面所有参数的计算结果都会作为计算所需的 state片段,传入最后一个参数中进行计算。createSelector( ... inputSelectors I [ inputSelectors] , resultFunc )当 Redux state变化时, al!ItemsSelector和 selectedldSelector都会执行,当它们的返回结果发生变化时, getltems 将会自动计算出最新的己选择条目。
当前后两次 inputs巳lectors (即 allitemsS巳lector和 selectedldSelector)计算所得结果没有发生变化时, resultFunc (即 getltems)将会直接返回缓存结果,因而规避了大量的计算
开销。