Appearance
vue 状态管理
1. 简介
1.1 为什么要有状态管理
- 解决组件间共享状态的复杂性 多个组件或嵌套层级较深的组件需要共享同一状态
- 避免数据流混乱与不一致性 分散的状态可能导致同一数据在不同组件中表现不一致,状态管理通过集中存储确保数据来源唯一性,降低数据冲突风险
- 提升代码可维护性与可预测性 集中化状态管理使数据变更路径清晰,便于代码维护与调试
- 支持异步操作与复杂逻辑处理 状态管理库(如 Vuex、Pinia)提供标准化异步操作处理(如 actions),避免直接在组件中耦合异步代码,提升逻辑复用性
总结: 为集中化管理数据,封装的轮子
1.2 状态管理提供什么功能
集中化存储 通过全局唯一的 store 统一管理应用核心状态
获取状态 实时获取 store 中的状态,支持响应式更新
状态变更 支持同步(mutations)和异步( actions)来修改 store状态
状态持久化与恢复 通过插件,支持从本地存储恢复状态
1.3 状态管理库的选择
Vuex | Pinia 未来趋势 |
|---|---|
| Vue 2 官方推荐状态管理库 | Vue 3 官方推荐状态管理库 |
| 支持TypeScript,但需额外配置 | 原生支持TypeScript,类型推断更完善 |
2. Vuex
js
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 引入模块
import animal from './animal'
export default new Vuex.Store({
/**
* 需要共享的状态
*
* vue2使用:this.$store.state[属性]
*/
state: {
count: 0,
name: '张三'
},
/**
* state 中状态不能直接修改
* 使用 mutations 来修改状态
*
* vue2使用:this.$store.commit('setCount', 2)
*/
mutations: {
/**
* @param {*} state 第一个参数是 Store 中的状态(必传)
* @param {*} newVal 第二个参数是传入的参数 (可选)
*/
setCount (state, newVal) {
state.count = newVal
}
setName (state, newVal) {
state.name = newVal
}
},
/**
* 和 mutations 类似,用来修改状态
* 区别: 异步处理,异步任务必须要使用 actions
*
* vue2使用:this.$store.dispatch('changeNameAsync', '李四')
*/
actions: {
/**
* @param {*} context 上下文对象,包含 commit 方法
* @param {*} newVal 传入的参数
*/
changeNameAsync (context, newVal) {
setTimeout(() => {
// 在这里调用 mutations 中的处理方法
context.commit('setName', newVal)
}, 2000)
}
},
/**
* 相当于计算属性
* 用来封装计算逻辑,有缓存的功能
* 只有当它的依赖状态发生改变,才会重新计算
*
* vue2使用:this.$store.getters.[属性]
*/
getters: {
/**
* @param {*} state 状态
* @returns {*} 返回值
*/
doubleCount: (state) => {
return state.count * 2
}
},
/**
* 避免在一个复杂的项目 state 中的数据变得臃肿,Vuex 允许将 Store 分成不同的模块
* 每个模块可以包含自己的 state、mutations、actions、getters
*
* vue2使用:this.$store.state.animal.[属性] 获取状态
* vue2使用:this.$store.commit('animal/setName', '李四') 修改状态
* vue2使用:this.$store.dispatch('animal/changeNameAsync', '李四') 异步修改状态
* vue2使用:this.$store.getters.animal.doubleCount 获取计算属性
*/
modules: {
// 模块
animal,
}
});3. Pinia
3.1 创建入口文件
js
// store/index.js
import { createPinia } from 'pinia'
const pinia = createPinia();
export default pinia;3.2 main.js引入
js
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
const app = createApp(App);
app.use(store);
app.mount('#app');3.3 创建状态
- 支持组合式和选项式
- 推荐选项式
js
// store/useCounter.js
import { defineStore } from 'pinia'
/**
* 定义一个 store
*
* @param {string} counter 唯一标识,必须是唯一的
* @param {object} options 配置选项
*/
const useCounter = defineStore('counter', {
/**
* 定义状态
*/
state: () => ({
count: 0,
name: '张三'
}),
/**
* 定义修改状态的方法
*/
actions: {
/**
* 修改state状态
* @param {*} newVal 传入的参数
*/
setCount(newVal) {
this.count = newVal
},
/**
* 异步修改state状态
* @param {*} newVal 传入的参数
*/
async setCountAsync(newVal) {
this.setCount(newVal)
},
},
/**
* 定义计算属性
*/
getters: {
doubleCount (state) => {
return state.count * 2
},
}
});
export default useCounter;3.4 组件中使用
vue
<script setup>
import { storeToRefs } from 'pinia';
import { useCounter } from './store/useCounter';
const storeCounter = useCounter();
// 通过pinia自带的方法,转换成ref,就是响应式的了
let {count, name} = storeToRefs(storeCounter)
// 获取值
console.log(storeCounter.count); // 0
// 修改值1 - 通过actions
storeCounter.setCount(1);
// 修改值2 - 直接修改
storeCounter.count = 2;
// 修改值3 - 通过patch
storeCounter.$patch({
count: 3,
name: '李四'
})
</script>3.5 持久化
可以自己存储,建议使用插件
pinia-plugin-persistedstate 是一个 Pinia 插件,用于实现状态的持久化存储。它可以将 Pinia 中的状态存储到本地存储(如 localStorage、sessionStorage)中,使得状态在页面刷新或关闭后仍然保持不变。
3.5.1 安装插件
bash
npm install pinia-plugin-persistedstate3.5.2 引入插件
js
// main.js
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
const app = createApp(App)
app.use(pinia)
app.mount('#app')3.5.3 使用
创建 Store 时,将 persist 选项设置为 true。
js
import { defineStore } from 'pinia'
export const useStore = defineStore(
'counter',
() => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
},
{
persist: true
}
)3.5.4 配置
默认配置
- 使用 localStorage 进行存储
- store.$id 作为 storage 默认的 key
- 使用 JSON.stringify/JSON.parse 进行序列化/反序列化
- 整个 state 默认将被持久化