走一遍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 + 1
case 'DECREMENT':
return state - 1
default:
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 + 1
case 'DECREMENT':
return state - 1
default:
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) {
// 备份一份reduers
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
}
...
// 返回一个方法 这个方法就是一个新的reducer
// 此时 action分发之后,都在这个新reducer中,遍历子reducer去调用执行更新
// 也就是说reducer更新的范围是局部的,且更新范围等于一个子reducer的state范围
let hasChange = false // 标记
const nextState = {} // 预备下一个状态的state
// 遍历所有子reducer
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) // 获取新的子state
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey // 对比新旧子state,只要右边,就全局state树标记为有改变
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length // 判断是否root state有变,有就返回新的root state 否则返回局部子state变化的根state
return hasChanged ? nextState : state
}
dispatch和reducer的关联过程
dispatch分发一个action的时候,触发reducer的工作在createStore中完成
export default function createStore(reducer) {
// 设置一个变量 指向当前需要执行的reducer 为什么不直接用 需要这样搞,因为有个replaceReducer功能,动态切换reducer
// 而currentReducer确保指向正确的那只
let currentReducer = reducer
// dispatch
function dispatch(action) {
// 执行reducer
currentState = 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和用法 这边就不细讲了