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。
版权声明:本文为原创文章,版权归 Mr. Ma's Blog 所有,转载请联系博主获得授权。
本文地址:https://www.misterma.com/archives/922/
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。