Vue3 状态管理工具 Pinia 的简单使用
Pinia 是 Vue 的一个状态管理工具,它的功能和 Vuex 差不多,主要用于 Vue3 的状态管理,但也可以用于 Vue2。目前 Vue 官方推荐的状态管理工具也是 Pinia,它相比 Vuex 来说,在扩展性和对 TypeScript 的支持要更好。
我这里搭配 Pinia 使用的是 Vue3,使用的写法是 script setup 组合式 API,如果你对 Vue3 的 script setup 的组合式 API 还不太了解的话,可以先看一下 Vue3 的 setup 语法糖 。
安装和使用
进入 Vue 项目目录,使用 npm 安装:
npm install pinia --save在 main.js 中引入和注册 Pinia:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
const pinia = createPinia(); // 创建 pinia
app.use(pinia); // 注册 pinia
app.mount('#app');编写 Store
Store 是 Pinia 的核心仓库,里面包含了你需要在应用中使用的各种状态和操作状态的方法。
为了方便管理,你可以在 src 目录中创建一个 store 目录,把 store 相关的 JS 文件都放到 store 目录中。
下面是一个简单的 store:
import { defineStore } from 'pinia';
export const useTestStore = defineStore('test', {
state() {
return {
count: 1,
text: '呵呵'
}
},
actions: {
chCount() {
this.count ++;
},
chText() {
this.text = '我的博客 misterma.com';
}
}
});你需要引入一个 defineStore 来创建 store。
在导出模块的时候,按照 Pinia 官方的命名规范,需要以 use 开头,store 结尾。
defineStore 的第一个参数 ID 是你的 store 仓库名称,第二个参数就是选项配置。在选项配置中 state 就是状态,你可以把需要存放的数据放到 state 中,它比较类似于组件中的 data ,写法也和组件中的 data 差不多。actions 就是操作 state 状态的方法,和组件中的 methods 比较类似。
我上面的 Store 中配置了 count 和 text 两个状态,在 actions 中编写了 chCount 和 chText 两个方法来更改 count 和 text 。
获取 Store 状态
在你需要使用 store 的组件中引入编写的 store,然后调用 store:
<template>
<div>
<p>{{ store.text }}</p>
<p>{{ store.count }}</p>
</div>
</template>
<script setup>
import { useTestStore } from './store';
const store = useTestStore();
// 在控制台输出 store 的状态
console.log(store.count);
console.log(store.text);
</script>这里获取的 store 是一个使用 reactive 包装的对象,在读取 store 状态的时候不需要在 value 中获取,可以直接读取状态。
如果你还不太了解 reactive 的话,可以看一下 Vue3 的 setup 语法糖 中的 响应式 。
更改 Store 状态
在实例化 store 后可以直接调用 actions 里的方法来更改状态:
<template>
<div>
<p>{{ store.text }}</p>
<p>{{ store.count }}</p>
<button @click="buttonClick">更改 Store 状态</button>
</div>
</template>
<script setup>
import { useTestStore } from './store';
const store = useTestStore();
function buttonClick() {
store.chCount();
store.chText();
}
</script>Action 相关
action 类似于组件里的 method,操作 state 状态的方法就写在 actions 中。
上面简单的写了 chText 和 chCount 两个方法来更改 state 里的 text 和 count ,更改的内容也是写死的,如果你需要动态获取内容更改的话,actions 里的方法也可以接收参数:
import { defineStore } from 'pinia';
export const useTestStore = defineStore('test', {
state() {
return {
count: 1,
text: '呵呵'
}
},
actions: {
chText(text) {
this.text = text;
}
}
});下面调用 actions 中的 chText 动态传入内容更改:
import { useTestStore } from './store';
const store = useTestStore();
store.chText('Hello');action 是支持异步操作的,你甚至可以在 action 中发送 HTTP 请求。
下面使用 Fetch 在 action 中发送 HTTP 请求,然后把请求到的内容传给 state 里的 text :
import { defineStore } from 'pinia';
export const useTestStore = defineStore('test', {
state() {
return {
text: '呵呵'
}
},
actions: {
async chText() {
// 发送 fetch 请求
const data = await fetch('data.txt');
// 把请求到的数据转换为普通 text 文本传给 state 的 text
this.text = await data.text();
}
}
});Getter
Getter 类似于组件里的 computed 计算属性,它可以对 state 里的数据做一些计算处理。
下面是一个简单的 store :
import { defineStore } from 'pinia';
export const useTestStore = defineStore('test', {
state() {
return {
userGroup: 1
}
}
});在 state 中包含一个 userGroup ,我需要根据这个 userGroup 的数字输出 1:普通用户 、2:管理员 、3:超级管理员 ,如果在组件中先获取 userGroup 的值在判断输出的话,可能会是下面这样:
<template>
<div>{{ userGroup }}</div>
</template>
<script setup>
import { useTestStore } from './store';
import { ref } from 'vue';
const store = useTestStore();
const userGroup = ref('');
if (store.userGroup === 1) {
userGroup.value = '普通用户';
}else if (store.userGroup === 2) {
userGroup.value = '管理员';
}else if (store.userGroup === 3) {
userGroup.value = '超级管理员';
}else {
userGroup.value = '账号异常';
}
</script>这种复杂的判断如果直接放到组件里的话,还是比较影响代码可读性的,而且如果多个组件都需要判断输出的话,也需要写很多遍。
下面把这些判断的代码放到 store 的 Getter 中:
import { defineStore } from 'pinia';
export const useTestStore = defineStore('test', {
state() {
return {
userGroup: 1
}
},
getters: {
userGroupName(state) {
if (state.userGroup === 1) {
return '普通用户';
} else if (state.userGroup === 2) {
return '管理员';
} else if (state.userGroup === 3) {
return '超级管理员';
} else {
return '账号异常';
}
}
}
});我在组件中只需要调用 getters 里的 userGroupName 就可以输出计算后的 userGroup :
<template>
<div>{{ store.userGroupName }}</div>
</template>
<script setup>
import { useTestStore } from './store';
const store = useTestStore();
</script>组合式 Setup Store
上面的 store 用的都是选项式的写法,Pinia 也支持组合式的写法,下面通过组合式编写一个简单的 store:
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useTestStore = defineStore('test', () => {
// 相当于 state
const userGroup = ref(1);
// 相当于 actions
function changeGroup(group) {
userGroup.value = group;
}
// 相当于 getters
const userGroupName = computed(() => {
if (userGroup.value === 1) {
return '普通用户';
} else if (userGroup.value === 2) {
return '管理员';
} else if (userGroup.value === 3) {
return '超级管理员';
} else {
return '账号异常';
}
});
// 把状态和方法暴露出去
return { userGroup, changeGroup, userGroupName };
});在组件中还是一样的调用:
<template>
<div>
{{ store.userGroup }}
{{ store.userGroupName }}
<button @click="store.changeGroup(3)">更改 userGroup</button>
</div>
</template>
<script setup>
import { useTestStore } from './store';
const store = useTestStore();
</script>在组合式写法中 ref 就是 state 状态,函数就是 actions 方法,computed 就是 getters 。
在 Vue2 中使用 Pinia
我这里使用的是 Vue2.7,可以直接使用 Pinia。Vue2.7 以下的版本因为没有 setup 组合式 API,用起来可能会比较麻烦。
在 main.js 中引入和注册 pinia:
import Vue from 'vue';
import App from './App.vue';
import { createPinia, PiniaVuePlugin } from 'pinia';
Vue.use(PiniaVuePlugin);
const pinia = createPinia();
new Vue({
render: h => h(App),
pinia
}).$mount('#app');在 Vue2 中除了引入 createPinia 外还需要引入 PiniaVuePlugin ,使用 Vue.use 注册 PiniaVuePlugin ,实例化 vue 的时候传入 createPinia 。
store 的编写和 Vue3 是一样的,下面是一个简单的 store:
import { defineStore } from 'pinia';
export const useTestStore = defineStore('test', {
state() {
return {
count: 1,
text: '在 Vue2 中使用 Pinia'
}
},
actions: {
chCount() {
this.count ++;
},
chText(text) {
this.text = text;
}
}
});下面在组件中使用 store:
<template>
<div id="app">
{{ store.text }}
{{ store.count }}
<button @click="buttonClick">更改count</button>
</div>
</template>
<script>
import { useTestStore } from './store';
export default {
name: 'App',
setup() {
// 实例化 store
const store = useTestStore();
// 把 store 暴露到 this
return { store };
},
methods: {
buttonClick() {
// 调用 store 中的 chCount 来更改 count
this.store.chCount();
}
}
}
</script>在要使用 store 的组件中引入编写的 store,在 setup 函数中实例化 store,然后把 store 暴露到 this 。
如果你的 Vue3 使用的是选项式 API 也可以用上面的方式使用 store。
版权声明:本文为原创文章,版权归 Changbin's Blog 所有,转载请联系博主获得授权。
本文地址:https://www.misterma.com/archives/922/
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。