Skip to content

状态管理

状态管理方案

内置状态管理

React 组件状态

jsx
import React, { useState, useEffect } from 'react'
import { View, Text, Button } from '@tarojs/components'

export default function Counter() {
  const [count, setCount] = useState(0)
  
  useEffect(() => {
    console.log('Count changed:', count)
  }, [count])
  
  const handleIncrement = () => {
    setCount(prevCount => prevCount + 1)
  }
  
  return (
    <View>
      <Text>Count: {count}</Text>
      <Button onClick={handleIncrement}>增加</Button>
    </View>
  )
}

Vue 组件状态

vue
<template>
  <view>
    <text>Count: {{ count }}</text>
    <button @click="handleIncrement">增加</button>
  </view>
</template>

<script>
export default {
  name: 'Counter',
  data() {
    return {
      count: 0
    }
  },
  methods: {
    handleIncrement() {
      this.count++
    }
  },
  watch: {
    count(newVal) {
      console.log('Count changed:', newVal)
    }
  }
}
</script>

全局状态管理

Redux

安装依赖

bash
npm install redux react-redux @tarojs/redux

创建 store

javascript
// store/index.js
import { createStore, combineReducers } from 'redux'

// 定义初始状态
const initialState = {
  count: 0,
  user: null
}

// 定义 reducer
function counterReducer(state = initialState.count, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    case 'RESET':
      return 0
    default:
      return state
  }
}

function userReducer(state = initialState.user, action) {
  switch (action.type) {
    case 'SET_USER':
      return action.payload
    case 'CLEAR_USER':
      return null
    default:
      return state
  }
}

// 组合 reducer
const rootReducer = combineReducers({
  counter: counterReducer,
  user: userReducer
})

// 创建 store
const store = createStore(rootReducer)

export default store

在应用中使用

javascript
// app.js
import React from 'react'
import { Provider } from 'react-redux'
import store from './store'
import Index from './pages/index/index'

export default function App() {
  return (
    <Provider store={store}>
      <Index />
    </Provider>
  )
}

在组件中使用

jsx
import React from 'react'
import { View, Text, Button } from '@tarojs/components'
import { useSelector, useDispatch } from 'react-redux'

export default function Counter() {
  const count = useSelector(state => state.counter)
  const dispatch = useDispatch()
  
  const handleIncrement = () => {
    dispatch({ type: 'INCREMENT' })
  }
  
  const handleDecrement = () => {
    dispatch({ type: 'DECREMENT' })
  }
  
  const handleReset = () => {
    dispatch({ type: 'RESET' })
  }
  
  return (
    <View>
      <Text>Count: {count}</Text>
      <Button onClick={handleIncrement}>增加</Button>
      <Button onClick={handleDecrement}>减少</Button>
      <Button onClick={handleReset}>重置</Button>
    </View>
  )
}

MobX

安装依赖

bash
npm install mobx mobx-react

创建 store

javascript
// store/counterStore.js
import { observable, action } from 'mobx'

class CounterStore {
  @observable count = 0
  
  @action increment() {
    this.count++
  }
  
  @action decrement() {
    this.count--
  }
  
  @action reset() {
    this.count = 0
  }
}

export default new CounterStore()

// store/userStore.js
import { observable, action } from 'mobx'

class UserStore {
  @observable user = null
  
  @action setUser(user) {
    this.user = user
  }
  
  @action clearUser() {
    this.user = null
  }
}

export default new UserStore()

在组件中使用

jsx
import React from 'react'
import { View, Text, Button } from '@tarojs/components'
import { observer } from 'mobx-react'
import counterStore from '../../store/counterStore'

export default observer(function Counter() {
  const handleIncrement = () => {
    counterStore.increment()
  }
  
  const handleDecrement = () => {
    counterStore.decrement()
  }
  
  const handleReset = () => {
    counterStore.reset()
  }
  
  return (
    <View>
      <Text>Count: {counterStore.count}</Text>
      <Button onClick={handleIncrement}>增加</Button>
      <Button onClick={handleDecrement}>减少</Button>
      <Button onClick={handleReset}>重置</Button>
    </View>
  )
})

Vuex

安装依赖

bash
npm install vuex

创建 store

javascript
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0,
    user: null
  },
  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    },
    reset(state) {
      state.count = 0
    },
    setUser(state, user) {
      state.user = user
    },
    clearUser(state) {
      state.user = null
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment')
      }, 1000)
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  }
})

在应用中使用

javascript
// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

在组件中使用

vue
<template>
  <view>
    <text>Count: {{ count }}</text>
    <text>Double Count: {{ doubleCount }}</text>
    <button @click="handleIncrement">增加</button>
    <button @click="handleDecrement">减少</button>
    <button @click="handleReset">重置</button>
    <button @click="handleIncrementAsync">异步增加</button>
  </view>
</template>

<script>
export default {
  name: 'Counter',
  computed: {
    count() {
      return this.$store.state.count
    },
    doubleCount() {
      return this.$store.getters.doubleCount
    }
  },
  methods: {
    handleIncrement() {
      this.$store.commit('increment')
    },
    handleDecrement() {
      this.$store.commit('decrement')
    },
    handleReset() {
      this.$store.commit('reset')
    },
    handleIncrementAsync() {
      this.$store.dispatch('incrementAsync')
    }
  }
}
</script>

Pinia

安装依赖

bash
npm install pinia

创建 store

javascript
// store/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
    reset() {
      this.count = 0
    },
    incrementAsync() {
      setTimeout(() => {
        this.count++
      }, 1000)
    }
  }
})

// store/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    user: null
  }),
  actions: {
    setUser(user) {
      this.user = user
    },
    clearUser() {
      this.user = null
    }
  }
})

在应用中使用

javascript
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
const pinia = createPinia()

app.use(pinia)
app.mount('#app')

在组件中使用

vue
<template>
  <view>
    <text>Count: {{ counter.count }}</text>
    <text>Double Count: {{ counter.doubleCount }}</text>
    <button @click="counter.increment">增加</button>
    <button @click="counter.decrement">减少</button>
    <button @click="counter.reset">重置</button>
    <button @click="counter.incrementAsync">异步增加</button>
  </view>
</template>

<script setup>
import { useCounterStore } from '../../store/counter'

const counter = useCounterStore()
</script>

状态管理最佳实践

状态设计原则

  1. 单一数据源:整个应用的状态应该集中在一个 store 中
  2. 状态只读:状态只能通过 action 来修改,不能直接修改
  3. 使用纯函数:reducer 应该是纯函数,不应该有副作用
  4. 合理划分状态:根据功能模块划分状态,避免状态过于集中
  5. 状态持久化:对于需要持久化的状态,应该使用 localStorage 或其他存储方式

性能优化

  1. 使用 memo:对于计算密集的操作,使用 useMemo 或 createSelector 进行缓存
  2. 避免不必要的渲染:使用 React.memo、shouldComponentUpdate 等方法避免不必要的渲染
  3. 批量更新:使用 batch 或其他方式批量更新状态,减少渲染次数
  4. 按需加载:对于大型应用,使用按需加载的方式减少初始包大小

调试技巧

  1. 使用 Redux DevTools:Redux DevTools 可以帮助调试状态变化
  2. 使用 MobX DevTools:MobX DevTools 可以帮助调试 MobX 状态
  3. 使用 Pinia DevTools:Pinia DevTools 可以帮助调试 Pinia 状态
  4. 添加日志:在关键的状态变化处添加日志,便于调试

常见问题

  1. 状态更新不生效:检查是否正确使用了 action 或 mutation
  2. 组件不重新渲染:检查是否正确使用了 connect 或 observer
  3. 状态过于复杂:考虑拆分状态,使用模块化的 store
  4. 性能问题:检查是否存在不必要的渲染,使用性能优化技巧

状态管理工具对比

工具优势劣势适用场景
Redux可预测性强、生态成熟代码冗余、学习成本高大型应用、需要严格状态管理的场景
MobX简洁易用、响应式更新可预测性较差中小型应用、快速开发
Vuex与 Vue 集成紧密、文档丰富灵活性较低Vue 应用
Pinia简洁易用、类型安全、与 Vue 3 集成良好生态相对较新Vue 3 应用

选择合适的状态管理方案

  1. 小型应用:使用组件内置状态即可
  2. 中型应用:使用 MobX 或 Pinia
  3. 大型应用:使用 Redux 或 Vuex
  4. Vue 应用:优先使用 Pinia(Vue 3)或 Vuex(Vue 2)
  5. React 应用:优先使用 Redux 或 MobX

基于 VitePress 的本地知识库