Redux的设计思想很简单,一、web应用是一个状态机,视图与状态一一对应;二、全部状态保存在一个对象里
复制代码
Store 应用状态的管理者,能够看做一个数据库,包含如下函数web
const store = {
dispatch(action), // 触发state改变的惟一方法
subscribe(listener), // 订阅函数,store变化触发的订阅的监听函数,返回取消订阅的方法
getState(), // 获取state的方法
replaceReducer(nextReducer), // 替换reducer
[$$observable]: observable
}
复制代码
一个应用只应有一个单一的 store,其管理着惟一的应用状态 state Redux提供createStore函数,用于生成store数据库
import { createStore } from 'redux';
const store = createStore(reducer);
复制代码
statejson
一个对象,包含全部数据,能够看做数据库中的数据,经过const state = store.getState()
获取redux
Actionapi
一个包含type属性的对象,做用是描述如何修改state,它会运输数据到store,做为reducer函数的参数数组
Action Creatorbash
生成Action的函数app
dispatch异步
view发出Action的惟一方法,redux规定不能直接在业务代码中修改state,若想要修改,只能经过store.dispatch(Action)实现async
Reducer
一个纯函数,会根据Acion的type值生成新的state替换调原来调state
只对关键代码进行解读。
index.ts
先看入口文件
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
//...
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}
复制代码
很简单,导出一个对象,接下来一个个分析
createStore.ts
import $$observable from './utils/symbol-observable'
import {
Store,
PreloadedState,
StoreEnhancer,
Dispatch,
Observer,
ExtendState
} from './types/store'
import { Action } from './types/actions'
import { Reducer } from './types/reducers'
import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'
export default function createStore (
reducer, // 由于dispatch会自动触发reducer的执行,因此在生成store的时候须要把reducer函数传递进来
preloadedState?, // 初始state
enhancer? // 加强,经过一系列中间件加强dispatch函数,例如对于一步Action对处理,后面详细介绍
) {
// ...对参数的判断
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
}
let currentReducer = reducer // reducer函数
let currentState = preloadedState // 当前state
let currentListeners = [] // 用于存储订阅的回调函数,dispatch 后逐个执行
let nextListeners = currentListeners // 下一个事件循环的回调函数数组
let isDispatching = false // 是否正在dispatch
// 判断当前的回调函数队列是否与下一个回调函数队列相同,不能影响本次的执行
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
// 获取state
function getState() {
// 直接返回state
return currentState
}
// 订阅--取消订阅
function subscribe(listener) {
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
// 取消订阅
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners()
// 将取消订阅的回调函数从数组中移除
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
}
// 派发一个Action用于改变state
// 若是dispatch的不是一个对象类型的action(同步),而是一个Promise/Thunk(异步),
// 就须要引入redux-thunk 等中间件来反转控制权,具体会在applyMiddlewares()方法中解析
function dispatch(action) {
// 确保是一个普通的对象,若非普通对象,原型链上会出现其余属性
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// 确保拥有type属性
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
// 是否正在dispatch
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
// dispatch 触发reducer函数,返回state,赋值给currentState,这也是为何咱们要在建立store对象的时候传入reducer
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 逐个执行订阅的回调函数
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
// 替换当前的reducer
function replaceReducer (
nextReducer
) {
// 直接替换,简单粗暴
currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
return store
}
// 这是留给 可观察/响应式库 的接口,具体实现能够自行搜索一下
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
}
function observeState() {
const observerAsObserver = observer
if (observerAsObserver.next) {
observerAsObserver.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
// 初始化state
dispatch({ type: ActionTypes.INIT })
// 返回store对象
const store = {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
return store
}
复制代码
createStore(reducer, preloadState?, enhancer?)是Redux的核心代码,它给咱们提供了获取state的方法getState()、触发state改变的方法dispatch(Action)等
combineReducers.ts
import { Reducer } from './types/reducers'
import { AnyAction, Action } from './types/actions'
import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'
import {
ReducersMapObject,
StateFromReducersMapObject,
ActionFromReducersMapObject
} from './types/reducers'
import { CombinedState } from './types/store'
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers: ReducersMapObject = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
}
// 全部reducer的key集合
const finalReducerKeys = Object.keys(finalReducers)
let unexpectedKeyCache
// 返回合成后的 reducer
return function combination(
state,
action
) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key] // 获取当前子的state
const nextStateForKey = reducer(previousStateForKey, action) // 执行起对应的reducer
nextState[key] = nextStateForKey // 将子nextState挂载到对应的键名
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state // 返回总的state
}
}
复制代码
假如咱们的应用有两个状态须要管理,一个是打印日志,另外一个是计数器功能,咱们能够把它们写在一个reducer中,但若是还有其余动做,这个堆在一个会让代码变得难以维护,因此最好但办法是拆分reducer,使每个动做变得单一,最后在整合在一块儿,combineReducers()就是为此而生的。须要注意的是,子reducer的名称应该与起对应的state相同。
好比咱们的目录结构以下
reducers/
├── index.js
├── counterReducer.js
├── todosReducer.js
复制代码
就能够这样实现
/* reducers/index.js */
import { combineReducers } from 'redux'
import counterReducer from './counterReducer'
import todosReducer from './todosReducer'
const rootReducer = combineReducers({
counter: counterReducer, // 键名就是该 reducer 对应管理的 state
todos: todosReducer
})
export default rootReducer
-------------------------------------------------
/* reducers/counterReducer.js */
export default function counterReducer(counter = 0, action) { // 传入的 state 实际上是 state.counter
switch (action.type) {
case 'INCREMENT':
return counter + 1 // counter 是值传递,所以能够直接返回一个值
default:
return counter
}
}
-------------------------------------------------
/* reducers/todosReducers */
export default function todosReducer(todos = [], action) { // 传入的 state 实际上是 state.todos
switch (action.type) {
case 'ADD_TODO':
return [ ...todos, action.payload ]
default:
return todos
}
}
复制代码
这样作的好处不言而喻。不管您的应用状态树有多么的复杂,均可以经过逐层下分管理对应部分的 state:
counterReducer(counter, action) -------------------- counter
↗ ↘
rootReducer(state, action) —→∑ ↗ optTimeReducer(optTime, action) ------ optTime ↘ nextState
↘—→∑ todo ↗
↘ todoListReducer(todoList,action) ----- todoList ↗
注:左侧表示 dispatch 分发流,∑ 表示 combineReducers;右侧表示各实体 reducer 的返回值,最后汇总整合成 nextState
复制代码
import { Dispatch } from './types/store'
import {
AnyAction,
ActionCreator,
ActionCreatorsMapObject
} from './types/actions'
function bindActionCreator<A extends AnyAction = AnyAction>(
actionCreator: ActionCreator<A>,
dispatch: Dispatch
) {
return function (this: any, ...args: any[]) {
// 给Action Creator 加上dispatch技能,也就是包裹一下
return dispatch(actionCreator.apply(this, args))
}
}
export default function bindActionCreators(
actionCreators,
dispatch
) {
// 处理actionCreators是函数的状况
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
// 处理actionCreators是对象的状况
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
复制代码
bindActionCreators(actionCreators, dispatch)这个函数的功能也比较单一,就是dispatch(ActionCreator(XXX))
,这个方法在异步状况下,没什么卵用。。。
export default function compose(...funcs) =>
// 复合函数 compose(func1, func2, func3)(0) => func3(func2(func1(0)))
export default function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
}
复制代码
这个函数很是简单,功能也很单一,主要用了数组的reduce方法,将函数做为reduce的回调函数的参数,参考文档
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
复制代码
中间件就是一个函数,对dispatch进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其余功能。
它是 Redux 的原生方法,做用是将全部中间件组成一个数组,依次执行。目的是为了在dispatch先后,统一处理想作对事。
import compose from './compose'
import { Middleware, MiddlewareAPI } from './types/middleware'
import { AnyAction } from './types/actions'
import { StoreEnhancer, StoreCreator, Dispatch } from './types/store'
import { Reducer } from './types/reducers'
export default function applyMiddleware(
...middlewares
) {
return (createStore) => (
reducer,
...args
) => {
const store = createStore(reducer, ...args)
let dispatch = () => {
}
// 中间件增长api
const middlewareAPI = {
getState,
dispatch
}
// chain存放中间件的数组,逐个增长api
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// compose复合函数 a(b(c(0))) => compose(a, b, c)(0)
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
从源码能够看出,这个函数的主要做用就是经过各类中间件对dispatch进行加强,借此咱们就能够处理异步Action等问题了。
咱们能够思考一下为何处理异步Action就须要对dispatch进行加强,缘由以下:
(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其余功能,也承担不了,由于理论上,纯函数不能进行读写操做。
(2)View:与 State 一一对应,能够看做 State 的视觉层,也不合适承担其余功能。
(3)Action:存放数据的对象,即消息的载体,只能被别人操做,本身不能进行任何操做。
因此只有发送 Action 的这个步骤,即store.dispatch()方法,能够添加功能。
同步操做只须要发出一种Action,异步操做则须要发出3种,发送时、成功时、失败时,例如:
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }
复制代码
一样的state也须要进行对应的改造
let state = {
// ...
isFetching: true, // 是否正在抓去数据
didInvalidate: true, // 数据是否过期
lastUpdated: 'xxxxxxx' // 更新数据时间
}
复制代码
因此异步操做的思路应该是:
前面说处处理异步Action须要借助到redux-thunk等中间件,如今具体分析一下redux-thunk是怎么处理异步请求的。
异步操做至少要送出两个 Action:用户触发第一个 Action,这个跟同步操做同样,没有问题;如何才能在操做结束时,系统自动送出第二个 Action 呢?
最主要的就是在Action Creator中处理,例如store.dispatch(fetchPosts(selectedPost))
fetchPosts()就是一个Action Creator,看一下内部实现:
const fetchPosts = postTitle => (dispatch, getState) => {
// 先发送第一个Action,同步的
dispatch(requestPosts(postTitle));
return fetch(`/some/API/${postTitle}.json`)
.then(response => response.json())
.then(json =>
// 请求到结果后,发送第二个Action,异步
dispatch(receivePosts(postTitle, json)
));
};
};
复制代码
值得注意的是,Action Creator返回的是一个函数,而正常返回应该是一个对象,而且这个函数的参数是dispatch和getState两个方法。
这个咱们就须要用到redux-thunk帮咱们处理一下,看下源码
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
复制代码
竟是如此简单的函数,这里咱们结合applyMiddleware分析一下:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
const store = createStore(
reducer,
{},
applyMiddleware(thunk)
);
复制代码
applyMiddleware函数的参数是thunk函数 ==>
({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
复制代码
回顾一下applyMiddleware函数 ==>
import compose from './compose'
import { Middleware, MiddlewareAPI } from './types/middleware'
import { AnyAction } from './types/actions'
import { StoreEnhancer, StoreCreator, Dispatch } from './types/store'
import { Reducer } from './types/reducers'
export default function applyMiddleware(
...middlewares
) {
return (createStore) => (
reducer,
...args
) => {
const store = createStore(reducer, ...args)
let dispatch = () => {
}
// 中间件增长api
const middlewareAPI = {
getState,
dispatch
}
// chain存放中间件的数组,逐个增长api
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// compose复合函数 a(b(c(0))) => compose(a, b, c)(0)
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
应用了redux-thunk,dispatch函数就会变成
dispatch = (store.dispatch) => (action) => {
// 处理action是函数的状况
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 处理action是对象的状况
return store.dispatch(action)
}
复制代码
总结了这些,若有不对的地方还望你们指正,但愿有兴趣的朋友一块儿交流交流。