Redux
Redux 是一个基于 Flux 架构和函数式编程(纯函数/不可变数据)的状态管理库。它的核心思想是:单一数据源、状态只读、使用纯函数(Reducer)来执行修改。通过严苛的规范,换取了极强的状态可预测性和时间旅行(Time Travel)调试能力。
1. 核心工作流与三大原则
Redux 的工作流非常严谨,是典型的单向数据流:
View (视图) -> 触发 Action (动作) -> Dispatch (派发) -> Reducer (纯函数计算) -> 返回新 State -> 触发视图更新
面试必考:Redux 的三大原则
- 单一数据源 (Single Source of Truth):整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
- State 是只读的 (Read-only):唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
- 使用纯函数来执行修改 (Changes are made with pure functions):为了描述 action 如何改变 state tree ,你需要编写 reducers。Reducer 必须是纯函数:即
(oldState, action) => newState,绝对不能修改原状态,必须返回一个全新的对象!
2. Redux 中间件 (Middleware) 原理
如果面试官问:“既然 Reducer 必须是纯函数,那网络请求、日志打印这种副作用 (Side Effects) 代码该写在哪里?” 答案是:中间件 (Middleware)。
中间件的核心本质是:劫持并重写 store.dispatch 方法。
在 action 发出之后,到达 reducer 之前,中间件可以拦截这个 action,进行异步请求、日志打印、甚至是取消派发。
高频手写题:实现 Redux 的
compose函数 Redux 底层通过compose将多个中间件洋葱模型般地嵌套起来。它的核心是利用Array.prototype.reduce。
function compose(...funcs) {
if (funcs.length === 0) return (arg) => arg;
if (funcs.length === 1) return funcs[0];
// 面试手写重点:利用 reduce 把前一个函数的执行结果作为后一个函数的入参
return funcs.reduce(
(a, b) =>
(...args) =>
a(b(...args)),
);
}
// 执行过程示例:
// compose(f, g, h)(x) === f(g(h(x)))
经典中间件对比:Redux-Thunk vs Redux-Saga
redux-thunk:- 原理:判断如果传给 dispatch 的 action 是一个函数,就把 dispatch 传给这个函数执行;如果是对象,就直接放行。
- 优点:极简,学习成本低。
- 缺点:把异步逻辑写成了回调地狱,极难处理复杂的竞态条件和任务取消。
redux-saga(大厂重度依赖):- 原理:基于 ES6 的 Generator 机制实现。
- 优点:将异步操作完全从业务逻辑中剥离,变成了“同步”的写法;提供了极度强大的控制流(如
takeLatest防抖、race竞态、cancel取消任务)。
3. react-redux 的底层实现
Redux 本身是框架无关的,能在 React 里丝滑使用,全靠 react-redux 库搭桥。
核心设计:Context + 发布订阅
<Provider>:在应用最顶层,通过 React Context 将store实例传递给整棵组件树。useSelector(或早期的connect):- 内部调用
store.getState()获取状态片段。 - 内部调用
store.subscribe()订阅 store 的变化。当状态改变时,比对当前片段是否发生变化,如果变了,强制当前组件重新渲染。
- 内部调用
面试加分项:React 18 下的重构 在早期的
react-redux中,由于使用了外部的发布订阅模式,在 React 的并发渲染(Concurrent Mode)下很容易出现“页面撕裂(Tearing)”(即同一个状态在页面上半部分和下半部分渲染出不同的值)。 为了解决这个问题,React 18 专门推出了一个官方 Hook:useSyncExternalStore。目前react-redux的底层已经全面切换到了这个 Hook,完美解决了外部状态库在并发模式下的撕裂问题。