Skip to content

几个状态管理库的原理分析

Redux

Redux 的设计思想

  1. 单一数据源:Redux 鼓励整个应用的状态被存储在一个单一的 JavaScript 对象中。使得应用的状态变得可预测且易于理解。

  2. 不可变数据:应用状态是只读的,唯一改变状态的方式是通过发起一个 action,一个描述发生了什么事情的普通 JavaScript 对象。

  3. 使用纯函数进行状态修改:Redux 中的状态修改通过纯函数完成,这些函数被称为 "reducers"

  4. 通过单向数据流管理状态: Redux 强调了单向数据流的概念。数据在应用中的流动是单向的,从应用状态到用户界面,再到用户交互产生的 action,再到 reducer 修改状态。这种清晰的数据流方向使得状态的变化易于追踪和调试。

  5. 使用中间件处理副作用:Redux 提供了中间件的概念,允许在 action 发起到 reducer 处理之间插入额外的逻辑。因此可以借助中间件处理异步操作、日志记录、错误处理等副作用。

  6. 预测性和调试性:由于 Redux 遵循着单一数据源和纯函数的原则,应用的状态变化变得可预测,可以追踪到状态的每一个变化。

Redux 的用法

  1. 创建初始值
js
// initialState.js
const initialState = {
  counter: 0,
  todos: []
}

export default initialState
// initialState.js
const initialState = {
  counter: 0,
  todos: []
}

export default initialState
  1. 创建 action

每个 action 必须包含 type 字段

js
// actions.js
export const increment = () => ({
  type: 'INCREMENT'
})

export const addTodo = text => ({
  type: 'ADD_TODO',
  payload: { text }
})
// actions.js
export const increment = () => ({
  type: 'INCREMENT'
})

export const addTodo = text => ({
  type: 'ADD_TODO',
  payload: { text }
})
  1. 编写 reducer 和合并 reduceers

Reducers 是纯函数,接收当前状态和一个 action,返回一个新的状态。

js
// reducers.js
const counterReducer = (state = initialState.counter, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    default:
      return state
  }
}

const todosReducer = (state = initialState.todos, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, { text: action.payload.text, completed: false }]
    default:
      return state
  }
}

export { counterReducer, todosReducer }
// reducers.js
const counterReducer = (state = initialState.counter, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    default:
      return state
  }
}

const todosReducer = (state = initialState.todos, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, { text: action.payload.text, completed: false }]
    default:
      return state
  }
}

export { counterReducer, todosReducer }
js
// rootReducer.js
import { combineReducers } from 'redux'
import { counterReducer, todosReducer } from './reducers'

const rootReducer = combineReducers({
  counter: counterReducer,
  todos: todosReducer
})

export default rootReducer
// rootReducer.js
import { combineReducers } from 'redux'
import { counterReducer, todosReducer } from './reducers'

const rootReducer = combineReducers({
  counter: counterReducer,
  todos: todosReducer
})

export default rootReducer
  1. 创建 store
js
// store.js
import { createStore } from 'redux'
import rootReducer from './rootReducer'
import initialState from './initialState'

const store = createStore(rootReducer, initialState)

export default store
// store.js
import { createStore } from 'redux'
import rootReducer from './rootReducer'
import initialState from './initialState'

const store = createStore(rootReducer, initialState)

export default store
  1. 在应用中挂载
jsx
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import CounterComponent from './CounterComponent'

ReactDOM.render(
  <Provider store={store}>
    <CounterComponent />
  </Provider>,
  document.getElementById('root')
)
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import CounterComponent from './CounterComponent'

ReactDOM.render(
  <Provider store={store}>
    <CounterComponent />
  </Provider>,
  document.getElementById('root')
)
  1. 连接 React 组件

使用 React-Redux 提供的 connect 函数将 React 组件连接到 Redux store,并将状态和 action 创建函数映射到组件的 props 上。

jsx
// CounterComponent.js
import React from 'react'
import { connect } from 'react-redux'
import { increment } from './actions'

const CounterComponent = ({ counter, increment }) => (
  <div>
    <p>Counter: {counter}</p>
    <button onClick={increment}>Increment</button>
  </div>
)

const mapStateToProps = state => ({
  counter: state.counter
})

const mapDispatchToProps = {
  increment
}

export default connect(mapStateToProps, mapDispatchToProps)(CounterComponent)
// CounterComponent.js
import React from 'react'
import { connect } from 'react-redux'
import { increment } from './actions'

const CounterComponent = ({ counter, increment }) => (
  <div>
    <p>Counter: {counter}</p>
    <button onClick={increment}>Increment</button>
  </div>
)

const mapStateToProps = state => ({
  counter: state.counter
})

const mapDispatchToProps = {
  increment
}

export default connect(mapStateToProps, mapDispatchToProps)(CounterComponent)

Mobx

Mobx 的设计思想

  1. 简单而可预测的状态树:使用简单的 js 对象来表示应用的状态,而不是深度嵌套的

  2. 可变状态:Mobx 认为可变状态是合理的,它不像 Redux 那样通过分发不可变的 action 来更新状态,Mobx 支持直接修改状态树的值,然后通过观察者模式自动通知订阅者更新。

  3. 响应式数据:Mobx 采用响应式编程的思想,通过依赖收集和派发更新,来构造依赖关系,无需开发者手动指定。(类似于 Vue)

  4. 通过计算属性派发状态:MobX 提供了 computed 属性,让开发者可以方便地创建派生状态。这些计算属性也会自动收集依赖,从而减少重复的计算开销。

  5. 最小化 UI:通过事务机制最小化 UI 重绘,只有事务结束时才会触发 UI 重绘。(微任务)

  6. 异步支持:Mobx 的 action 支持异步操作,并且在异步过程中处理响应的变化。

Mobx 的特性

  1. 观察者模式mobx-react 使用观察者模式来追踪状态的变化。当状态发生变化时,它会通知所有观察者(React 组件)执行相应的更新

  2. 装饰器mobx-react 使用装饰器(Decorator)语法来简化状态管理的语法。装饰器是一种将额外行为附加到类或类的成员的语法糖。在 mobx-react 中,@observer 装饰器用于将 React 组件转化为观察者,使其能够响应 Mobx 状态的变化。

jsx
import { observer, inject } from 'mobx-react'

@observer
@inject('appStore')
class MyComponent extends React.Component {
  // ...
}
import { observer, inject } from 'mobx-react'

@observer
@inject('appStore')
class MyComponent extends React.Component {
  // ...
}
  1. 上下文 APImobx-react 使用 React 的 Context API 来使 Mobx 的状态在整个应用中可访问。MobXProviderContext 提供了一个根组件,负责传递 Mobx store。
jsx
import { Provider } from 'mobx-react'

const Root = ({ store }) => (
  <Provider store={store}>{/* Your app components go here */}</Provider>
)
import { Provider } from 'mobx-react'

const Root = ({ store }) => (
  <Provider store={store}>{/* Your app components go here */}</Provider>
)
  1. 依赖收集和取消订阅:当一个组件通过 @observer 被转化为观察者时,它会自动订阅相关 Mobx store 中的状态。当组件被销毁时,它也会自动取消订阅,以避免内存泄漏。

  2. 强制更新mobx-react 使用 forceUpdate 来强制组件进行更新。这是因为 Mobx 不会改变 React 组件的状态,而是通过观察者模式触发更新。当 Mobx 收集到的依赖的状态发生变化时,forceUpdate 用于通知 React 组件重新渲染。

  3. 性能优化mobx-react 使用了一些技巧来优化性能,比如将类成员包装成可观察对象,使用微任务(microtask)来异步触发更新,以及使用 computed 属性来缓存计算结果等。

Vuex

Polar