走一遍redux
核心原理图
action
本身action触发的更新是同步的 分发action,到接收,到执行都是函数执行,并没有异步监听
ActionTypes.INIT
初始化action,用于各种检查,state合法性等
普通action
const action = {type: 'update-userinfo',payload: {} // 挂载更新内容}
reducer
本质上是一个函数
// 函数签名// 接收两个参数,一个是state,当前的状态树,一个是action,携带挂载type Reducer<S = any, A extends Action = AnyAction> = (state: S | undefined,action: A)// 示例 返回的state状态树会更新到对应的root状态树位置上function updateUser(state, { type, payload }) {if (type === 'update-user') {return Object.assign({}, state, { username: payload.username })}// 无更新return state}
combineReducer
理论上可以不需要combineReducer,单个reducer可以干翻全场,不过如果项目很大且复杂,建议用上这玩意儿
单个reducer的示例 官方示例
function counter(state, action) {// 自动初始化if (typeof state === 'undefined') {return 0}switch (action.type) {case 'INCREMENT':return state + 1case 'DECREMENT':return state - 1default:return state}}var store = Redux.createStore(counter)
多个reducer combine的简单示例
function counter(state, action) {// 自动初始化if (typeof state === 'undefined') {return 0}switch (action.type) {case 'INCREMENT':return state + 1case 'DECREMENT':return state - 1default:return state}}function user(state, action) {// 自动初始化if (typeof state === 'undefined') {return '李白'}switch (action.type) {case '1':return '中国人'case '2':return '外国人'default:return state}}const reduers = combineReducer({user, counter}) // 注意 是一个对象var store = Redux.createStore(counter)
如果是单个reducer,分发action后,直接调用reducer,带上预置state执行reducer,返回一个state,这个state有可能是新的,有可能没有变,但是都作为返回值,更新到外层的树上
combineReducer的实现
export default function combineReducers(reducers) {// 备份一份reduersfor (let i = 0; i < reducerKeys.length; i++) {const key = reducerKeys[i]}...// 返回一个方法 这个方法就是一个新的reducer// 此时 action分发之后,都在这个新reducer中,遍历子reducer去调用执行更新// 也就是说reducer更新的范围是局部的,且更新范围等于一个子reducer的state范围let hasChange = false // 标记const nextState = {} // 预备下一个状态的state// 遍历所有子reducerfor (let i = 0; i < finalReducerKeys.length; i++) {const key = finalReducerKeys[i]const reducer = finalReducers[key]const previousStateForKey = state[key] // 获取上个状态的子stateconst nextStateForKey = reducer(previousStateForKey, action) // 获取新的子stateif (typeof nextStateForKey === 'undefined') {const errorMessage = getUndefinedStateErrorMessage(key, action)throw new Error(errorMessage)}nextState[key] = nextStateForKeyhasChanged = hasChanged || nextStateForKey !== previousStateForKey // 对比新旧子state,只要右边,就全局state树标记为有改变}hasChanged =hasChanged || finalReducerKeys.length !== Object.keys(state).length // 判断是否root state有变,有就返回新的root state 否则返回局部子state变化的根statereturn hasChanged ? nextState : state}
dispatch和reducer的关联过程
dispatch分发一个action的时候,触发reducer的工作在createStore中完成
export default function createStore(reducer) {// 设置一个变量 指向当前需要执行的reducer 为什么不直接用 需要这样搞,因为有个replaceReducer功能,动态切换reducer// 而currentReducer确保指向正确的那只let currentReducer = reducer// dispatchfunction dispatch(action) {// 执行reducercurrentState = currentReducer(currentState, action)}return action // 返回的是action本身}
中间件applyMiddleware的应用
// 假设有这样一个中间件组合 例子const middleware = [function log() {}, function timetick() {}, function filter() {}]// 需要先进后出 也就是filter中间件先执行// 理论上是建议中间件之间不要有顺序耦合,不过redux的中间件执行也是有顺序的// 根据官方公式 compose(f, g, h)应该得到 (...args) => f(g(h(...args)))// 这样就确保了中间件的执行和中间件集合的实现组合export default function compose(...funcs) {// 如果只有一个中间件 不用组合 直接放回if (funcs.length === 0) {return funcs[0]}return funcs.reduce((a, b) => (...args) => a(b(...args)))}// 用上面那个例子来展示compose([log, timetick, filter])(dispatch)=> [log, timetick, filter].reduce(...)=> log(timetick(...args))=> log(timetick(filter(...args)))=> log(timetick(filter(dispatch)))// 所以要求 每个中间件都应该返回dispatch,便于后续中间件能拿到dispatch
还有其他一些redux的api和用法 这边就不细讲了
