唉,现在的内部分享都卷起来了,不弄个源码解读,都不好意拿出来讲
redux基本用法
import { createStore } from 'redux'
// 这是一个reducer
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
let store = createStore(counterReducer)
store.subscribe(() => console.log(store.getState()))
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}
createStore
createStore 会生成唯一的store
// reducer如上文中的reducer 函数,preloadedState 初始状态,enhancer 是用来做扩展的
export default function createStore(reducer, preloadedState?, enhancer?) {
// ...校验
// 当前store中的reducer
let currentReducer = reducer
// 当前store中存储的状态
let currentState = preloadedState as S
// 当前store中放置的监听函数
let currentListeners: (() => void)[] | null = []
let nextListeners = currentListeners
let isDispatching = false
// 获取state
function getState(): S {
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 as S
}
// 在每次订阅和取消订阅的时候,会让 nextListeners 和 currentListeners 不是同一个引用
// 这里要是不明白,可看 ensureCanMutateNextListeners 的理解
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
// 订阅
function subscribe(listener: () => void) {
// ...校验
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/store#subscribelistener for more details.'
)
}
isSubscribed = false
// 从下一轮的监听函数数组(用于下一次dispatch)中删除这个监听器。
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
}
// dispatch,想要改变state只有通过dispatch
function dispatch(action: A) {
if (!isPlainObject(action)) {
throw new Error(
`Actions must be plain objects. Instead, the actual type was: '${kindOf(
action
)}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
// 主要是预防的是在reducer中做dispatch操作,
// 如果在reduder中做了dispatch,而dispatch又必然会导致reducer的调用,就会造成死循环。
try {
isDispatching = true
// 更新 state
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 在每次 dispatch的时候,当 reducer执行完毕,订阅执行前,
// 让 nextListeners 和 currentListeners 是一个引用
const listeners = (currentListeners = nextListeners)
// 执行所有添加到store中的监听函数
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
/**
* replaceReducer是替换当前的reducer的函数,replaceReducer接受一个新的reducer,
* 替换完成之后,会执行 dispatch({ type: ActionTypes.INIT }) ,用来初始化store的状态。
* replaceReducer的三种使用场景
* 当你的程序要进行代码分割的时候
* 当你要动态的加载不同的reducer的时候
* 当你要实现一个实时reloading机制的时候
*/
function replaceReducer<NewState, NewActions extends A>(
nextReducer: Reducer<NewState, NewActions>
): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext {
if (typeof nextReducer !== 'function') {
throw new Error(
`Expected the nextReducer to be a function. Instead, received: '${kindOf(
nextReducer
)}`
)
}
// TODO: do this more elegantly
;(currentReducer as unknown as Reducer<NewState, NewActions>) = nextReducer
dispatch({ type: ActionTypes.REPLACE } as A)
// change the type of the store by casting it to the new store
return store
}
// 这个方法用于提供观察者模式的操作
function observable() {
const outerSubscribe = subscribe
return {
/**
* The minimal observable subscription method.
* @param observer Any object that can be used as an observer.
* The observer object should have a `next` method.
* @returns An object with an `unsubscribe` method that can
* be used to unsubscribe the observable from the store, and prevent further
* emission of values from the observable.
*/
subscribe(observer: unknown) {
function observeState() {
const observerAsObserver = observer as Observer<S>
// 观察者模式的链式结构,传入当前的state
if (observerAsObserver.next) {
observerAsObserver.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
}
ensureCanMutateNextListeners 的理解
这两个例子对照着看
let currentListeners = []
let nextListeners = currentListeners
function subscribe(fn) {
nextListeners.push(fn)
}
function dispatch() {
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
}
subscribe(() => {
console.log('1')
// 在订阅里面增加了订阅
subscribe(() => {
console.log('2')
})
})
// 最后两个订阅都执行了,输出 1、2
// 这是因为在for循环的时候,中途改变数组的长度,循环的次数也会发生变化
dispatch()
let currentListeners = []
let nextListeners = currentListeners
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
function subscribe(fn) {
ensureCanMutateNextListeners()
// 这时候 nextListeners 和 currentListeners 已经不相等了,因为不是同一个引用
console.log(nextListeners === currentListeners) // false
nextListeners.push(fn)
}
function dispatch() {
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
}
subscribe(() => {
console.log('1')
// 在订阅里面增加了订阅
subscribe(() => {
console.log('2')
})
})
// 输出 1
dispatch()
combineReducers
combineReducers把子reducer合并成一个总的reducer。
// 基本用法
let state = {
counter: {
count: 0
},
info: {
name: '你好哇',
description: '哈哈哈哈!'
},
info1: {
name: '我不好',
description: '嘿嘿嘿嘿!'
}
}
/*counterReducer, 一个子reducer*/
/*注意:counterReducer 接收的 state 是 state.counter*/
function counterReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1
}
case 'DECREMENT':
return {
...state,
count: state.count - 1
}
default:
return state;
}
}
/*InfoReducer,一个子reducer*/
/*注意:InfoReducer 接收的 state 是 state.info*/
function InfoReducer(state, action) {
switch (action.type) {
case 'SET_NAME':
return {
...state,
name: action.name
}
case 'SET_DESCRIPTION':
return {
...state,
description: action.description
}
default:
return state;
}
}
// 注意,这里的counter和info要和state中的key相同
const reducer = combineReducers({
counter: counterReducer,
info: InfoReducer
});
combineReducers中没有info1对应的reducers,combineReducers返回的state就没有info1
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]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
// finalReducers是过滤后的reducers,确保它的每一个属性都是一个function
const finalReducerKeys = Object.keys(finalReducers)
// 返回合并后的新的reducer函数
return function combination(state, action) {
// 标志state是否有变化
let hasChanged = false
const nextState: StateFromReducersMapObject<typeof reducers> = {}
// 遍历执行所有的reducers,整合成为一个新的state
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
// 之前的 key 的 state
const previousStateForKey = state[key]
// 执行某一个reducer,获得新的state
const nextStateForKey = reducer(previousStateForKey, action)
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
return hasChanged ? nextState : state
}
}
compose
compose 的作用是把 [A, B, C] 转换成 A(B(C(next)))
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) => a(b(...args)))
}
嵌套箭头函数
// redux官方文档middleware里面的示例代码
const loggerMiddleware = storeAPI => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', storeAPI.getState())
return result
}
// 上面的代码可以翻译为下面的这段代码
function logger(store) {
return function wrapDispatchToAddLogging(next) {
return function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}
其实箭头函数的嵌套,就是柯里化。对于上面的 loggerMiddleware 函数,它有 3 个箭头, 这个函数要被调用 3 次 loggerMiddleware (storeAPI)(next)(action),前两次调用只是在传递参数,只有最后依次调用才会返回 {xxx} 代码段的返回值
middleware
中间件就是对dispatch的扩展。比如现在有两个需求,记录日志和记录异常,现在通过重写store.dispatch 来实现。
// 第一步
const store = createStore(reducer);
const next = store.dispatch;
const loggerMiddleware = (action) => {
console.log('this state', store.getState());
console.log('action', action);
next(action);
console.log('next state', store.getState());
}
store.dispatch = (action) => {
try {
console.log('exceptionMiddleware before', store.getState());
loggerMiddleware(action);
console.log('exceptionMiddleware after', store.getState());
} catch (err) {
console.error('错误报告: ', err)
}
}
这样写,loggerMiddleware 写死了,如果再多个需求就没法变动了。所以现在需要把方法提取出来。
// 第二步
const store = createStore(reducer);
const next = store.dispatch;
const loggerMiddleware = (next) => (action) => {
console.log('this state', store.getState());
console.log('action', action);
next(action);
console.log('next state', store.getState());
}
const exceptionMiddleware = (next) => (action) => {
try {
console.log('exceptionMiddleware before', store.getState());
next(action);
console.log('exceptionMiddleware after', store.getState());
} catch (err) {
console.error('错误报告: ', err)
}
}
store.dispatch = exceptionMiddleware(loggerMiddleware(next));
发现loggerMiddleware 中还有store这个变量,所以进一步提取。
// 第三步
const store = createStore(reducer);
const next = store.dispatch;
const loggerMiddleware = (store) => (next) => (action) => {
console.log('this state', store.getState());
console.log('action', action);
next(action);
console.log('next state', store.getState());
}
const exceptionMiddleware = (store) => (next) => (action) => {
try {
console.log('exceptionMiddleware before', store.getState());
next(action);
console.log('exceptionMiddleware after', store.getState());
} catch (err) {
console.error('错误报告: ', err)
}
}
const logger = loggerMiddleware(store);
const exception = exceptionMiddleware(store);
store.dispatch = exception(logger(next));
如果现在增加一个需求,在打印日志之前输出当前的时间戳
const timeMiddleware = (store) => (next) => (action) => {
console.log('time', new Date().getTime());
next(action);
}
...
const time = timeMiddleware(store);
store.dispatch = exception(time(logger(next)));
其实我们只需要知道这三个中间件,剩下的比如exception(time(logger(next))) 等可以封装起来。redux中是用applyMiddleware 来实现的,首先看下applyMiddleware的用法
/*接收旧的 createStore,返回新的 createStore*/
const newCreateStore = applyMiddleware(
exceptionMiddleware,
timeMiddleware,
loggerMiddleware
)(createStore);
/*返回了一个 dispatch 被重写过的 store*/
const store = newCreateStore(reducer);
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState) => { // 返回重写后新的 createStore
/*1. 生成store*/
const store = createStore(reducer, preloadedState)
let dispatch = () => {}
const middlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
/*给每个 middleware 传下store,相当于 const logger = loggerMiddleware(store);*/
/* const chain = [exception, time, logger]*/
const chain = middlewares.map(middleware => middleware(middlewareAPI))
/* 实现 exception(time((logger(dispatch))))*/
dispatch = compose(...chain)(store.dispatch)
// 返回store,用改造后的dispatch方法替换store中的dispatch
return {
...store,
dispatch
}
}
}
实际上applyMiddleware的用法是基于createStore的第三个参数enhancer
const middlewareEnhancer = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware)
const store = createStore(rootReducer, {}, middlewareEnhancer)
// createStore 内部的enhancer
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error(
`Expected the enhancer to be a function. Instead, received: '${kindOf(
enhancer
)}'`
)
}
return enhancer(createStore)(reducer, preloadedState)
}
bindActionCreators
bindActionCreators很少用得到。它是通过闭包,把dispatch 和 actionCreator 隐藏起来,让其他地方感知不到 redux 的存在。
// 核心的代码在这里,通过闭包隐藏了 actionCreator 和 dispatch
function bindActionCreator(actionCreator, dispatch) {
return function (this, ...args) {
return dispatch(actionCreator.apply(this, args))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
// actionCreators 必须是 function 或者 object
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, but instead received: '${kindOf(
actionCreators
)}'. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}