走一遍redux

核心原理图

action

本身action触发的更新是同步的 分发action,到接收,到执行都是函数执行,并没有异步监听

ActionTypes.INIT

初始化action,用于各种检查,state合法性等

普通action

  1. const action = {
  2. type: 'update-userinfo',
  3. payload: {} // 挂载更新内容
  4. }

reducer

本质上是一个函数

  1. // 函数签名
  2. // 接收两个参数,一个是state,当前的状态树,一个是action,携带挂载
  3. type Reducer<S = any, A extends Action = AnyAction> = (
  4. state: S | undefined,
  5. action: A
  6. )
  7. // 示例 返回的state状态树会更新到对应的root状态树位置上
  8. function updateUser(state, { type, payload }) {
  9. if (type === 'update-user') {
  10. return Object.assign({}, state, { username: payload.username })
  11. }
  12. // 无更新
  13. return state
  14. }

combineReducer

理论上可以不需要combineReducer,单个reducer可以干翻全场,不过如果项目很大且复杂,建议用上这玩意儿

单个reducer的示例 官方示例

  1. function counter(state, action) {
  2. // 自动初始化
  3. if (typeof state === 'undefined') {
  4. return 0
  5. }
  6. switch (action.type) {
  7. case 'INCREMENT':
  8. return state + 1
  9. case 'DECREMENT':
  10. return state - 1
  11. default:
  12. return state
  13. }
  14. }
  15. var store = Redux.createStore(counter)

多个reducer combine的简单示例

  1. function counter(state, action) {
  2. // 自动初始化
  3. if (typeof state === 'undefined') {
  4. return 0
  5. }
  6. switch (action.type) {
  7. case 'INCREMENT':
  8. return state + 1
  9. case 'DECREMENT':
  10. return state - 1
  11. default:
  12. return state
  13. }
  14. }
  15. function user(state, action) {
  16. // 自动初始化
  17. if (typeof state === 'undefined') {
  18. return '李白'
  19. }
  20. switch (action.type) {
  21. case '1':
  22. return '中国人'
  23. case '2':
  24. return '外国人'
  25. default:
  26. return state
  27. }
  28. }
  29. const reduers = combineReducer({user, counter}) // 注意 是一个对象
  30. var store = Redux.createStore(counter)

如果是单个reducer,分发action后,直接调用reducer,带上预置state执行reducer,返回一个state,这个state有可能是新的,有可能没有变,但是都作为返回值,更新到外层的树上

combineReducer的实现

  1. export default function combineReducers(reducers) {
  2. // 备份一份reduers
  3. for (let i = 0; i < reducerKeys.length; i++) {
  4. const key = reducerKeys[i]
  5. }
  6. ...
  7. // 返回一个方法 这个方法就是一个新的reducer
  8. // 此时 action分发之后,都在这个新reducer中,遍历子reducer去调用执行更新
  9. // 也就是说reducer更新的范围是局部的,且更新范围等于一个子reducer的state范围
  10. let hasChange = false // 标记
  11. const nextState = {} // 预备下一个状态的state
  12. // 遍历所有子reducer
  13. for (let i = 0; i < finalReducerKeys.length; i++) {
  14. const key = finalReducerKeys[i]
  15. const reducer = finalReducers[key]
  16. const previousStateForKey = state[key] // 获取上个状态的子state
  17. const nextStateForKey = reducer(previousStateForKey, action) // 获取新的子state
  18. if (typeof nextStateForKey === 'undefined') {
  19. const errorMessage = getUndefinedStateErrorMessage(key, action)
  20. throw new Error(errorMessage)
  21. }
  22. nextState[key] = nextStateForKey
  23. hasChanged = hasChanged || nextStateForKey !== previousStateForKey // 对比新旧子state,只要右边,就全局state树标记为有改变
  24. }
  25. hasChanged =
  26. hasChanged || finalReducerKeys.length !== Object.keys(state).length // 判断是否root state有变,有就返回新的root state 否则返回局部子state变化的根state
  27. return hasChanged ? nextState : state
  28. }

dispatch和reducer的关联过程

dispatch分发一个action的时候,触发reducer的工作在createStore中完成

  1. export default function createStore(reducer) {
  2. // 设置一个变量 指向当前需要执行的reducer 为什么不直接用 需要这样搞,因为有个replaceReducer功能,动态切换reducer
  3. // 而currentReducer确保指向正确的那只
  4. let currentReducer = reducer
  5. // dispatch
  6. function dispatch(action) {
  7. // 执行reducer
  8. currentState = currentReducer(currentState, action)
  9. }
  10. return action // 返回的是action本身
  11. }

中间件applyMiddleware的应用

  1. // 假设有这样一个中间件组合 例子
  2. const middleware = [function log() {}, function timetick() {}, function filter() {}]
  3. // 需要先进后出 也就是filter中间件先执行
  4. // 理论上是建议中间件之间不要有顺序耦合,不过redux的中间件执行也是有顺序的
  5. // 根据官方公式 compose(f, g, h)应该得到 (...args) => f(g(h(...args)))
  6. // 这样就确保了中间件的执行和中间件集合的实现组合
  7. export default function compose(...funcs) {
  8. // 如果只有一个中间件 不用组合 直接放回
  9. if (funcs.length === 0) {
  10. return funcs[0]
  11. }
  12. return funcs.reduce((a, b) => (...args) => a(b(...args)))
  13. }
  14. // 用上面那个例子来展示
  15. compose([log, timetick, filter])(dispatch)
  16. => [log, timetick, filter].reduce(...)
  17. => log(timetick(...args))
  18. => log(timetick(filter(...args)))
  19. => log(timetick(filter(dispatch)))
  20. // 所以要求 每个中间件都应该返回dispatch,便于后续中间件能拿到dispatch

还有其他一些redux的api和用法 这边就不细讲了