Appearance
状态管理
状态管理方案
内置状态管理
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>状态管理最佳实践
状态设计原则
- 单一数据源:整个应用的状态应该集中在一个 store 中
- 状态只读:状态只能通过 action 来修改,不能直接修改
- 使用纯函数:reducer 应该是纯函数,不应该有副作用
- 合理划分状态:根据功能模块划分状态,避免状态过于集中
- 状态持久化:对于需要持久化的状态,应该使用 localStorage 或其他存储方式
性能优化
- 使用 memo:对于计算密集的操作,使用 useMemo 或 createSelector 进行缓存
- 避免不必要的渲染:使用 React.memo、shouldComponentUpdate 等方法避免不必要的渲染
- 批量更新:使用 batch 或其他方式批量更新状态,减少渲染次数
- 按需加载:对于大型应用,使用按需加载的方式减少初始包大小
调试技巧
- 使用 Redux DevTools:Redux DevTools 可以帮助调试状态变化
- 使用 MobX DevTools:MobX DevTools 可以帮助调试 MobX 状态
- 使用 Pinia DevTools:Pinia DevTools 可以帮助调试 Pinia 状态
- 添加日志:在关键的状态变化处添加日志,便于调试
常见问题
- 状态更新不生效:检查是否正确使用了 action 或 mutation
- 组件不重新渲染:检查是否正确使用了 connect 或 observer
- 状态过于复杂:考虑拆分状态,使用模块化的 store
- 性能问题:检查是否存在不必要的渲染,使用性能优化技巧
状态管理工具对比
| 工具 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Redux | 可预测性强、生态成熟 | 代码冗余、学习成本高 | 大型应用、需要严格状态管理的场景 |
| MobX | 简洁易用、响应式更新 | 可预测性较差 | 中小型应用、快速开发 |
| Vuex | 与 Vue 集成紧密、文档丰富 | 灵活性较低 | Vue 应用 |
| Pinia | 简洁易用、类型安全、与 Vue 3 集成良好 | 生态相对较新 | Vue 3 应用 |
选择合适的状态管理方案
- 小型应用:使用组件内置状态即可
- 中型应用:使用 MobX 或 Pinia
- 大型应用:使用 Redux 或 Vuex
- Vue 应用:优先使用 Pinia(Vue 3)或 Vuex(Vue 2)
- React 应用:优先使用 Redux 或 MobX