Redux 是可预测的状态管理框架,它很好的解决多交互,多数据源的诉求。
三大原则:
单一数据源:
整个应用的state被存储在一颗object tree中,并且这颗 object tree存在于唯一的store里,store就是存储数据的容器,容器里面会维护整个应用的state。store提供四个API:dispatch subscribe getState replaceReducer。 数据源是唯一的,所以获取数据的唯一方式就是store.getState()。
state只读:
根据state只读原则,state的变更要通过store的dispatch(action)方法,action就是变更数据的载体,action = {type: '', payload},type是变更数据的唯一标志, payload是需要变更的数据。
使用纯函数变更state
reducer是纯函数,接受两个参数即 state和action, 根据action的type属性执行对应的代码,更改相应的数据,然后返回新的state。
完整的数据流过程:
view层触发actionCreator, actionCreator通过store.dispatch(action) ,reducer执行对应的代码,返回新的state,更新页面。
image.png
源码结构:
src
├── utils ---------------------------------------- 工具函数
├── applyMiddleware.js --------------------------- 加载 middleware
├── bindActionCreators.js ------------------------ 生成将 action creator 包裹在 dispatch 里的函数
├── combineReducers.js --------------------------- 合并 reducer 函数
├── compose.js ----------------------------------- 组合函数
├── createStore.js ------------------------------- 创建一个 Redux store 来储存应用中所有的 state
├── index.js ------------------------------------- 入口 js
index.js
index.js文件是整个代码的入口,该文件暴露了一些接口供开发者使用: createStore, combineReducers, bindActionCreators, applyMiddleware, compose,接下来逐个分析这些接口。
createStore
createStore是redux最重要的API,它创建一个store,保存应用中的state。
createStore函数接受三个参数:reducer, preloadedState, enhancer,
返回值:dispatch、subscribe、getState、replaceReducer 和 [$$observable],这就是我们开发中主要使用的几个接口。
参数
reducer: 函数,返回下一个状态,接收state和action作为参数
preloadedState: state初始值,可以忽略不传
enhancer:store的增强器,一般是第三方中间件,返回一个增强后的store creator,这个函数通常由applyMiddleware函数来生成。
返回值
dispatch
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// 判断 action 是否有 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.')
}
// 对抛出 error 的兼容,但是无论如何都会继续执行 isDispatching = false 的操作
try {
isDispatching = true
// 使用 currentReducer 来操作传入 当前状态和 action,返回处理后的状态
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
}
dispatch函数是触发动作的函数,接收action行为作为参数,action必须是一个对象,且该对象必须有type参数,如果action满足条件,则调用currentReducer(其实就是createStore的参数 reducer的引用),currentReducer会根据action来改变对应的值,生成新的store,然后再遍历nextListeners列表,调用每一个监听函数。
getState
// 读取由 store 管理的状态树
function getState() {
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
}
return currentState
}
这个函数可以获取当前的状态,createStore 中的 currentState 储存当前的状态树,这是一个闭包,这个参数会持久存在,并且所有的操作状态都是改变这个引用,getState 函数返回当前的 currentState。
subscribe
function subscribe(listener) {
// 判断传入的参数是否为函数
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
}
if (isDispatching) {
throw new Error(
'You may not call store.subscribe() while the reducer is executing. ' +
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
除去一些边界条件的判断,subscribe函数最主要的是给store状态添加监听函数,该函数接收一个函数作为参数,会往nextListeners监听列表加入这个函数,然后会返回一个unsubscribe函数,用于解绑,如果解绑,就从nextListeners列表中去掉该函数,一旦调用dispatch改变store,监听函数就会全部执行。
combineReducers
reducer是管理state的一个模块,在应用初始化的时候,它返回initialState,当用户调用action时,它会根据action的type属性,进行相应的更新,reducer是纯函数,不会更改传入的state,会返回新的state。
当所有的reducer逻辑都写在同一个reducer函数里面会非常的庞大,所以我们会将reducer进行适当的拆分,但是最终传入createStore里面的是唯一的reducer函数,所以我们传入createStore前要进行合并,就需要combineReducers方法。
参数
reducers (Object): 一个对象,它的值(value)对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。
返回值
(Function): 它是真正 createStore 函数的 reducer,接受一个初始化状态和一个 action 参数;每次调用的时候会去遍历 finalReducer(有效的 reducer 列表),然后调用列表中每个 reducer,最终构造一个与 reducers 对象结构相同的 state 对象。
combineReducers用法
rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
// This would produce the following state object
{
potato: {
// ... potatoes, and other state managed by the potatoReducer ...
},
tomato: {
// ... tomatoes, and other state managed by the tomatoReducer, maybe some nice sauce? ...
}
}
源码
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
/* if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}*/
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
/*let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
let shapeAssertionError
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}*/
return function combination(state = {}, action) {
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
}
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]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
combineReducer函数返回一个combination 函数,该函数接收state和action作为参数,每次调用该函数时:
1、 for (let i = 0; i < finalReducerKeys.length; i++) { ... }:
遍历 finalReducer(有效的 reducer 列表);
2、 var previousStateForKey = state[key]:
当前遍历项的之前状态,看到这里就应该明白传入的 reducers
组合为什么 key
要和 store 里面的 state 的 key
相对应了;
3、 var nextStateForKey = reducer(previousStateForKey, action):
当前遍历项的下一个状态;
4、 nextState[key] = nextStateForKey:
将 当前遍历项的下一个状态添加到 nextState;
5、 hasChanged = hasChanged || nextStateForKey !== previousStateForKey:
判断状态是否改变;
6、 return hasChanged ? nextState : state:
如果没有改变就返回原有状态,如果改变了就返回新生成的状态对象。
applyMiddleware
bindActionCreators
- actionCreator创建动作
在分析之前先明确ActionCreator是什么?ActionCreator是动作创造者或者说是动作工厂,如果我们想根据不同的参数来生成不同值的计数器,例子如下:
const counterActionCreator = (step) => {
return {
type: 'increment',
step: step || 1,
}
}
2.bindActionCreator
从上述例子出发,如果我们想生成不同的计数器,并分发他们,则需要按照以下写法:
const action1 = counterActionCreator();
dispatch(action1);
const action2 = counterActionCreator(2);
dispatch(action2);
const action3 = counterActionCreator(3);
dispatch(action3);
从上面的写法来看,每次都需要去调用actionCreator,再调用dispatch分发action,会产生很多繁杂重复的代码,所以我们可以采用 bindActionCreators.js 文件里面的bindActionCreator 方法来优化代码,
bindActionCreator 源码:
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
bindActionCreator会返回一个函数,函数中会调用actionCreator生成action,并将action作为dispatch的参数分发掉,所以我们将action的生成动作和调用动作封装到了一个函数里面,我们直接调用bindActionCreator返回的函数就行,不用再每次去调用actionCreator,再调用dispatch分发action,例子如下:
const increment = bindActionCreator(counterActionCreator, dispatch);
increment();
increment(2);
increment(3);
- bindActionCreators
接下来看bindActionCreators函数,其实是对bindActionCreator的增强,去掉一些判断条件,源码部分具体如下:
function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') { // #1
return bindActionCreator(actionCreators, dispatch) // #2
}
....
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') { // #3
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
当actionCreators是一个函数时,直接调用bindActionCreator将整个过程封装返回即可
当actionCreators是一个有多个函数方法组成的对象时,遍历该对象,如果键对应的值是函数,则调用bindActionCreator将整个过程封装,并将封装结果赋值给该键,最后返回一个对象,对象里面的值都被bindActionCreator封装过。
actionCreators为对象时的例子如下:
const actionCreators = {
increment: function(step) {
return {
type: 'INCREMENT',
step: step || 1
}
},
decrement: function(step) {
return {
type: 'DECREMENT',
step: - (step || 1)
}
}
}
const newActionCreators = bindActionCreators(MyActionCreators, dispatch)
newActionCreators.increment();
newActionCreators.increment(1);
newActionCreators.decrement();
newActionCreators.decrement(1);
网友评论