乐闻世界logo
搜索文章和话题

Redux

Redux 是一个流行的 JavaScript 状态管理库,主要用于管理复杂应用的状态。它由 Dan Abramov 和 Andrew Clark 创建,并受到了 Flux 架构的启发。Redux 的核心理念是维护一个单一的全局状态对象,所有的状态变更都通过一种叫做“action”的方式来描述,然后这些 action 会通过“reducer”函数来更新状态。
Redux
MobX 和 Redux 的区别是什么,如何选择?MobX 和 Redux 都是流行的状态管理库,但它们的设计理念和使用方式有很大的不同。选择哪一个取决于项目需求、团队偏好和具体场景。 ## 核心设计理念 ### MobX - **基于观察者模式**:自动追踪状态变化,无需手动订阅 - **命令式编程**:直接修改状态,更符合直觉 - **透明响应式**:状态变化自动触发更新 - **灵活性高**:不强制特定的代码结构 ### Redux - **基于函数式编程**:使用纯函数处理状态变化 - **声明式编程**:通过 dispatch action 来修改状态 - **单向数据流**:Action → Reducer → Store → View - **规范性高**:强制特定的代码结构 ## 代码对比 ### MobX 示例 ```javascript import { observable, action, computed, makeAutoObservable } from 'mobx'; class TodoStore { todos = []; filter = 'all'; constructor() { makeAutoObservable(this); } @computed get filteredTodos() { switch (this.filter) { case 'completed': return this.todos.filter(todo => todo.completed); case 'active': return this.todos.filter(todo => !todo.completed); default: return this.todos; } } @action addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } @action toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } @action setFilter(filter) { this.filter = filter; } } const store = new TodoStore(); // 使用 store.addTodo('Learn MobX'); store.toggleTodo(store.todos[0].id); ``` ### Redux 示例 ```javascript import { createStore } from 'redux'; // Action Types const ADD_TODO = 'ADD_TODO'; const TOGGLE_TODO = 'TOGGLE_TODO'; const SET_FILTER = 'SET_FILTER'; // Action Creators const addTodo = (text) => ({ type: ADD_TODO, payload: { id: Date.now(), text, completed: false } }); const toggleTodo = (id) => ({ type: TOGGLE_TODO, payload: id }); const setFilter = (filter) => ({ type: SET_FILTER, payload: filter }); // Reducer const initialState = { todos: [], filter: 'all' }; function todoReducer(state = initialState, action) { switch (action.type) { case ADD_TODO: return { ...state, todos: [...state.todos, action.payload] }; case TOGGLE_TODO: return { ...state, todos: state.todos.map(todo => todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo ) }; case SET_FILTER: return { ...state, filter: action.payload }; default: return state; } } const store = createStore(todoReducer); // Selector const selectFilteredTodos = (state) => { switch (state.filter) { case 'completed': return state.todos.filter(todo => todo.completed); case 'active': return state.todos.filter(todo => !todo.completed); default: return state.todos; } }; // 使用 store.dispatch(addTodo('Learn Redux')); store.dispatch(toggleTodo(store.getState().todos[0].id)); ``` ## 详细对比 | 特性 | MobX | Redux | |------|------|-------| | **编程范式** | 命令式、面向对象 | 函数式、声明式 | | **状态追踪** | 自动追踪 | 手动订阅 | | **状态修改** | 直接修改 | 通过 action | | **代码量** | 较少 | 较多 | | **学习曲线** | 平缓 | 陡峭 | | **灵活性** | 高 | 低 | | **规范性** | 低 | 高 | | **调试工具** | MobX DevTools | Redux DevTools | | **时间旅行** | 有限支持 | 完整支持 | | **中间件** | 不需要 | 丰富(redux-thunk、redux-saga 等) | | **性能** | 自动优化 | 需要手动优化 | | **状态结构** | 可以嵌套 | 推荐扁平化 | | **类型支持** | 良好 | 需要额外配置 | ## 使用场景 ### 适合使用 MobX 的场景 1. **快速开发**:需要快速原型开发或小型项目 2. **复杂状态结构**:状态结构复杂且嵌套 3. **团队经验**:团队更熟悉面向对象编程 4. **灵活性优先**:需要更多的代码灵活性 5. **学习成本**:希望降低学习成本 ```javascript // MobX 适合复杂嵌套状态 class UserStore { @observable user = { profile: { name: '', email: '', address: { city: '', country: '' } }, preferences: { theme: 'light', language: 'en' } }; @action updateCity(city) { this.user.profile.address.city = city; // 直接修改嵌套属性 } } ``` ### 适合使用 Redux 的场景 1. **大型项目**:需要严格规范的大型项目 2. **团队协作**:多人协作,需要统一的代码规范 3. **时间旅行**:需要完整的时间旅行调试功能 4. **中间件需求**:需要使用丰富的中间件生态 5. **函数式编程**:团队偏好函数式编程范式 ```javascript // Redux 适合扁平化状态 const initialState = { users: { byId: {}, allIds: [] }, profiles: { byId: {}, allIds: [] }, addresses: { byId: {}, allIds: [] } }; // 通过 reducer 处理状态变化 function reducer(state = initialState, action) { switch (action.type) { case UPDATE_CITY: return { ...state, addresses: { ...state.addresses, byId: { ...state.addresses.byId, [action.payload.id]: { ...state.addresses.byId[action.payload.id], city: action.payload.city } } } }; default: return state; } } ``` ## 性能对比 ### MobX 性能优势 1. **自动优化**:自动追踪依赖,只更新必要的组件 2. **批量更新**:action 内部的状态变化会被批量处理 3. **懒计算**:computed 值只在被访问时才计算 ```javascript // MobX 自动优化 class Store { @observable items = []; @computed get expensiveValue() { console.log('Computing expensive value'); return this.items.reduce((sum, item) => sum + item.value, 0); } } // 只有在访问 expensiveValue 时才会计算 console.log(store.expensiveValue); // 计算一次 console.log(store.expensiveValue); // 使用缓存,不计算 ``` ### Redux 性能挑战 1. **手动优化**:需要使用 reselect、memo 等工具优化 2. **全量比较**:每次 dispatch 都会比较整个状态树 3. **需要手动订阅**:需要手动选择需要的数据 ```javascript // Redux 需要手动优化 import { createSelector } from 'reselect'; const selectItems = (state) => state.items; const selectExpensiveValue = createSelector( [selectItems], (items) => { console.log('Computing expensive value'); return items.reduce((sum, item) => sum + item.value, 0); } ); // 需要手动选择数据 const value = selectExpensiveValue(store.getState()); ``` ## 调试对比 ### MobX 调试 ```javascript // 使用 MobX DevTools import { makeObservable, observable, action } from 'mobx'; class Store { @observable count = 0; constructor() { makeObservable(this); } @action increment() { this.count++; } } // 在浏览器中查看状态变化 ``` ### Redux 调试 ```javascript // 使用 Redux DevTools import { createStore } from 'redux'; const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); // 可以查看完整的 action 历史、状态变化和时间旅行 ``` ## 迁移建议 ### 从 MobX 迁移到 Redux 1. **重构状态结构**:将嵌套状态扁平化 2. **创建 action types**:定义所有可能的 action 3. **编写 reducers**:将状态修改逻辑移到 reducers 4. **使用中间件**:根据需要添加中间件 5. **更新组件**:使用 useSelector 和 useDispatch ### 从 Redux 迁移到 MobX 1. **创建 stores**:将 reducer 逻辑转换为 stores 2. **使用 observable**:将状态转换为 observable 3. **添加 actions**:将 action creators 转换为 actions 4. **更新组件**:使用 observer 或 useObserver 5. **简化代码**:移除不必要的样板代码 ## 总结 **选择 MobX 如果:** - 需要快速开发 - 状态结构复杂且嵌套 - 团队更熟悉面向对象编程 - 希望降低学习成本 - 需要更多的代码灵活性 **选择 Redux 如果:** - 项目规模较大 - 需要严格的代码规范 - 需要完整的时间旅行调试 - 需要丰富的中间件生态 - 团队偏好函数式编程 两者都是优秀的状态管理库,选择哪一个应该基于项目需求和团队情况。在实际项目中,也可以根据不同模块的特点混合使用。
前端 · 2月22日 14:08
MobX 和 Redux 有什么区别,应该如何选择?MobX 和 Redux 是两种流行的状态管理库,它们在设计理念和使用方式上有显著差异: ## 架构设计 **Redux**: - 采用单向数据流架构 - 遵循严格的不可变性原则 - 使用纯函数(reducers)来处理状态更新 - 状态是只读的,只能通过 dispatch action 来修改 - 需要手动选择需要的状态(通过 useSelector) **MobX**: - 采用响应式编程架构 - 允许可变状态,但通过 observable 进行追踪 - 可以直接修改状态(在 action 中) - 自动追踪依赖关系,自动更新相关组件 - 无需手动选择状态,组件自动订阅所需数据 ## 代码量和复杂度 **Redux**: - 需要编写大量的样板代码(actions、action creators、reducers) - 需要配置 store、middleware、reducers - 代码结构相对复杂,学习曲线陡峭 **MobX**: - 代码量少,简洁直观 - 最小化配置,开箱即用 - 学习曲线平缓,容易上手 ## 性能 **Redux**: - 通过 shallowEqual 进行浅比较来决定是否重新渲染 - 需要开发者手动优化性能(如使用 reselect) - 对于大型应用,可能需要额外的优化策略 **MobX**: - 细粒度的依赖追踪,只更新真正需要更新的组件 - 自动缓存计算属性,避免不必要的计算 - 性能优化是自动的,开发者无需过多关注 ## TypeScript 支持 **Redux**: - 需要为 actions、reducers、state 等定义类型 - 类型定义相对复杂,但类型安全性高 - 需要使用类型断言或类型守卫 **MobX**: - 类型推断更自然,类型定义更简单 - 与 TypeScript 集成更流畅 - 可以充分利用 TypeScript 的类型推断能力 ## 调试和可预测性 **Redux**: - 状态变化完全可预测,易于调试 - Redux DevTools 提供强大的时间旅行调试功能 - 所有的状态变化都通过 action 记录 **MobX**: - 调试相对复杂,因为状态可以在多处修改 - MobX DevTools 提供了调试支持,但不如 Redux 强大 - 需要遵循最佳实践(如使用 action)来提高可预测性 ## 适用场景 **选择 Redux**: - 需要严格的状态管理规范 - 团队规模大,需要明确的代码结构 - 需要时间旅行调试 - 状态变化逻辑复杂,需要中间件支持 **选择 MobX**: - 追求开发效率和代码简洁性 - 项目规模中小型 - 需要快速原型开发 - 团队对函数式响应式编程更熟悉 ## 总结 Redux 更适合需要严格架构和可预测性的大型项目,而 MobX 更适合追求开发效率和简洁性的项目。选择哪种库应该根据项目需求、团队经验和长期维护考虑来决定。
前端 · 2月21日 15:50
Redux 如何实现自定义中间件在Redux中,中间件是一种强大的机制,允许开发者在action被发送到reducer之前插入自己的逻辑。创建自定义的Redux中间件涉及到编写一个函数,该函数按照Redux中间件API的规格返回一个满足特定签名的函数。 我将向您展示如何自定义实现一个简单的日志中间件,该中间件的作用是在action被派发时在控制台输出日志信息。 以下是自定义Redux中间件的基本步骤: 1. 编写一个函数,该函数接收`store`的`dispatch`和`getState`方法。 2. 该函数返回一个接收下一个中间件的`next`函数的函数。 3. 返回的函数再返回一个接收action的函数。 4. 在最内层的函数体内,可以执行自定义的逻辑,然后调用`next(action)`将action传递给链中的下一个中间件或reducer。 下面是一个自定义日志中间件的例子: ```javascript // 自定义日志中间件 const loggerMiddleware = store => next => action => { // 自定义的逻辑:在当前action被处理之前输出日志 console.log('dispatching', action); // 调用链中的下一个中间件或reducer let result = next(action); // 自定义的逻辑:在action被处理后输出新的状态 console.log('next state', store.getState()); // 返回result,因为middleware的链需要从next(action)获取返回值 return result; }; export default loggerMiddleware; ``` 在上述的中间件代码中: - `store`: Redux store实例,它包含了`dispatch`和`getState`方法。 - `next`: 是一个将action传递给链中下一个处理者(中间件或reducer)的函数。 - `action`: 是当前正在处理的action对象。 使用这个中间件的典型方式是在创建Redux store时应用它: ```javascript import { createStore, applyMiddleware } from 'redux'; import rootReducer from './reducers'; import loggerMiddleware from './middleware/loggerMiddleware'; // 使用applyMiddleware来增强store,添加自定义的loggerMiddleware const store = createStore( rootReducer, applyMiddleware(loggerMiddleware) ); export default store; ``` 在这个例子中,任何派发到store的action都会先经过`loggerMiddleware`这个中间件,在控制台输出action信息,然后继续沿中间件链传递,直到最终被reducer处理。 这只是自定义中间件的一个简单例子,但您可以根据需要在中间件中实现更复杂的逻辑,例如异步操作、路由导航或其他您想要的任何自定义行为。
前端 · 2024年8月5日 12:48