Pinia 集中式状态存储
# Pinia 集中式状态存储
在现代前端开发中,尤其是在 Vue 3 中,Pinia 提供了一个简洁且功能强大的集中式状态管理解决方案。它被认为是 Vuex 的替代品,具有更简单的 API 和更好的 TypeScript 支持。让我们深入了解 Pinia 如何帮助我们管理全局状态。
# 1. 理解状态
在 Vue 中,状态(State) 是指在应用中需要共享的数据。举个例子,假设你有一个用户登录状态,这个状态在多个页面间共享。一旦用户登录成功,所有的页面都能够读取到当前登录的用户信息。
在 Vue 2 中,我们通常使用 Vuex 来实现集中式状态管理。而在 Vue 3 中,Pinia 是推荐的状态管理库,提供了更简洁、现代的 API。如果你在使用 Vue 2,那么 Vuex 和 Pinia 不能同时使用。
# 2. Vuex 简单示例
Vuex 是 Vue 官方的状态管理库,主要用于 Vue 2.x,但 Vue 3 也支持 Vuex。它的设计理念是集中式存储,所有的状态都存储在单一的 store 中,组件通过 commit
和 dispatch
来修改状态。Vuex 适合需要复杂状态管理和大型应用,但它的 API 相对繁琐,尤其是与 TypeScript 集成时。
让我们先来看看 Vuex 在 Vue 3 中的使用方式。
# 安装 Vuex
npm install vuex@next
# 创建 Vuex Store
// store/index.ts
import { createStore } from 'vuex';
const store = createStore({
state: {
username: '--',
},
getters: {
getUsername(state) {
return state.username.toUpperCase();
},
},
mutations: {
changeUsername(state, value: string) {
if (value && value.length < 10) {
state.username = value;
}
},
},
actions: {
changeUsernameAsync({ commit }, value: string) {
setTimeout(() => {
commit('changeUsername', value);
}, 1000);
},
},
});
export default store;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 在 Vue 中使用 Vuex
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
const app = createApp(App);
app.use(store);
app.mount('#app');
<!-- App.vue -->
<template>
<div>
<h1>Hello {{ username }}</h1>
<button @click="changeUsername">Change Username</button>
</div>
</template>
<script lang="ts">
import { computed } from 'vue';
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();
const username = computed(() => store.getters.getUsername);
const changeUsername = () => {
store.dispatch('changeUsernameAsync', 'NewUser');
};
return { username, changeUsername };
},
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 3. 创建 Pinia Store
Store 在 Pinia 中类似于 Vuex 中的 store,它用于保存应用的全局状态。我们可以在创建 Vue 应用时引入 Pinia,或者手动引入。
# 3.1 安装 Pinia
首先,需要安装 Pinia:
npm install pinia
然后,在 main.ts
文件中创建并引入 Pinia:
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
// 创建 Pinia 实例
const pinia = createPinia();
// 使用 Pinia
const app = createApp(App);
app.use(pinia);
app.mount('#app');
2
3
4
5
6
7
8
9
10
11
# 3.2 创建 Store
使用 defineStore
函数来定义一个 Store。Store 中包含三个主要部分:
- state:存储数据,相当于 Vuex 中的 state。
- getter:计算属性,用于获取或计算派生状态,相当于 Vuex 中的 getters。
- action:方法,用于改变 state,处理业务逻辑,相当于 Vuex 中的 actions。
下面是一个创建用户信息管理的例子:
// store/user.ts
import { defineStore } from 'pinia';
export const useUserStore = defineStore('userStore', {
// state - 用于存储数据
state: () => ({
username: '--'
}),
// getters - 用于读取计算的值
getters: {
getUsername: (state) => state.username.toUpperCase()
},
// actions - 用于修改 state 的方法
actions: {
changeUsername(value: string) {
if (value && value.length < 10) {
this.username = value;
}
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
state
用于存储数据,这里存储了username
。getter
用于计算并返回username
的大写版本。action
用于修改username
。
# 3.3 使用 Store 操作数据
Pinia 的使用非常简洁。在 Vue 组件中,我们只需要通过 useUserStore
获取 Store 实例,然后可以通过 state
、getter
和 action
来操作数据。
<template>
<div id="app">
<h1>Hello {{ userInfo.username }}</h1>
</div>
</template>
<script lang="ts" setup>
import { useUserStore } from "@/store/user";
// 获取 store
const userStore = useUserStore();
// 直接修改 state
userStore.username = 'Roy';
// 使用 action 修改 state
userStore.changeUsername('Alice');
// 获取 getter
console.log(userStore.getUsername); // 输出:ALICE
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4. storeToRefs
声明响应式数据
当你需要在 Vue 模板中访问 Pinia Store 的状态时,可以使用 storeToRefs
来将 Store 中的数据转化为响应式数据。这样做可以确保 Vue 模板能够正确地反应数据的变化。
import { storeToRefs } from 'pinia';
import { useUserStore } from "@/store/user";
// 获取 store
const userStore = useUserStore();
// 使用 storeToRefs 将 store 转为响应式数据
const { username } = storeToRefs(userStore);
console.log(username.value); // 输出 'Roy'
2
3
4
5
6
7
8
9
10
storeToRefs
与 Vue 中的 toRefs
函数类似,但它只会把 store 中的状态部分转为响应式数据,而 toRefs
会将整个对象转为响应式。
# 5. 修改 Store 数据
在实际应用中,通常我们会通过 action 来修改数据,因为它可以确保业务逻辑的一致性。你可以使用 patch
方法直接修改 state
,或者使用 action
来修改。
// 通过 action 修改数据
userStore.changeUsername('Roy');
// 直接修改 state
userStore.username = 'Alice';
// 使用 patch 批量修改
userStore.$patch({
username: 'New User'
});
2
3
4
5
6
7
8
9
10
# 6. Store 的混合式写法
除了传统的对象式 API 外,Pinia 还支持 组合式 API 的写法。这种方式让你可以更灵活地使用 Pinia,尤其是在处理复杂状态管理时。
import { defineStore } from 'pinia';
import { reactive } from 'vue';
// 使用组合式 API 定义 store
export const useUserStore = defineStore('userStore', () => {
// state
const userInfo = reactive({ username: '---' });
// action
function changeUsername(value: string) {
if (value && value.length < 10) {
userInfo.username = value;
}
}
// getter
function getUsername(): string {
return userInfo.username.toUpperCase();
}
return { userInfo, changeUsername, getUsername };
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在 Vue 组件中使用时,可以直接解构返回的对象:
<script lang="ts" setup>
import { useUserStore } from "@/store/user2";
// 获取 store
const { userInfo, changeUsername, getUsername } = useUserStore();
// 修改 store 数据
changeUsername('Roy');
// 获取 store 数据
console.log(getUsername()); // 输出:ROY
</script>
2
3
4
5
6
7
8
9
10
11
12
# 7. 总结
Pinia 是一个简单且直观的状态管理库,它提供了:
state
用于存储应用的状态。getter
用于获取计算后的状态。action
用于修改state
并处理业务逻辑。
它非常适合与 Vue 3 一起使用,相比 Vuex,它的 API 更加简洁,易于使用,且对 TypeScript 支持更好。在大型应用中,Pinia 也能够轻松地扩展和维护状态逻辑。