Pinia 是 Vue.js 的轻量级状态管理库
官方网站:pinia.vuejs.org/
中文文档(非官方,翻译了大部分内容):pinia.web3doc.top
为什么学习pinia?
- pinia和vuex4一样,也是vue官方的状态管理工具(作者是 Vue 核心团队成员)
- pinia相比vuex4,对于vue3的兼容性更好
- pinia相比vuex4,具备完善的类型推荐
- pinia同样支持vue开发者工具,最新的开发者工具对vuex4支持不好
- Pinia 的 API 设计非常接近
Vuex 5的提案。
pinia核心概念
- state: 状态
- actions: 修改状态(包括同步和异步,pinia中没有mutations)
- getters: 计算属性
基本使用与state
目标:掌握pinia的使用步骤
(1)安装
yarn add pinia
# or
npm i pinia
(2)在main.js中挂载pinia
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')
(3)新建文件store/index.js
import { defineStore } from "pinia";
// 创建store,命名规则: useXxxxStore
// 参数1:store的唯一 id
// 参数2:对象,可以提供state actions getters
const useCounterStore = defineStore("counter", {
// 数据 data
state: () => {
return {
count: 100,
};
},
// 计算 cpmputed
getters: {},
// 方法 methods
actions: {},
});
export default useCounterStore;
(4) 在组件中使用
<script setup>
import useCounterStore from './store';
const counterStore = useCounterStore()
</script>
<template>
<h1>Hello vue3</h1>
<div>{{ counterStore }}</div>
<!-- 不需要 .state.count,可以直接读取到数据,和vue2语法一样 -->
<div>{{ counterStore.count }}</div>
</template>
<style scoped>
</style>
getters的使用
pinia中的getters和vuex中的基本是一样的,也带有缓存的功能
(1)在getters中提供计算属性
import { defineStore } from "pinia";
// 创建store,命名规则: useXxxxStore
// 参数1:store的唯一 id
// 参数2:对象,可以提供state actions getters
const useCounterStore = defineStore("counter", {
// 数据 data
state: () => {
return {
count: 100,
// 有同名属性,会以 getters 为准
double: 500,
};
},
// 计算 cpmputed
getters: {
double() {
// 不用再写state,通过 this 使用即可
return this.count * 2;
},
},
// 方法 methods
// 没有 mutations,同步异步都可以在 actions 完成
actions: {
add() {
this.count++;
},
addAsync() {
setTimeout(() => {
this.count++;
}, 2000);
},
},
});
export default useCounterStore;
(2)在组件中使用
<!-- 不需要 .getters.double,可以直接读取到数据 -->
<div>{{ counterStore.double }}</div>
actions的使用
目标:掌握pinia中actions的使用
在pinia中没有mutations,只有actions,不管是同步还是异步的代码,都可以在actions中完成。
(1)在actions中提供方法并且修改数据
import { defineStore } from 'pinia'
// 1. 创建store
// 参数1:store的唯一表示
// 参数2:对象,可以提供state actions getters
const useCounterStore = defineStore('counter', {
actions: {
add() {
this.count++;
},
addNum(val) {
this.count+=val;
},
addAsync() {
setTimeout(() => {
this.count++;
}, 2000);
},
},
})
export default useCounterStore
(2)在组件中使用
<template>
<button @click="counterStore.add">同步相加</button>
<button @click="counterStore.addNum(10)">传参相加</button>
<button @click="counterStore.addAsync">异步相加</button>
</template>
storeToRefs的使用
目标:掌握storeToRefs的使用
如果直接从pinia中解构数据,会丢失响应式, 使用storeToRefs可以保证解构出来的数据也是响应式的
<script setup>
import { storeToRefs } from 'pinia';
import { toRefs } from 'vue';
import useCounterStore from './store';
const counterStore = useCounterStore()
// 非响应式
// const { count } = counterStore
// 响应式,用我就对了
const { count } = toRefs(counterStore)
// 响应式,pinia提供,减少不必要的响应式属性和方法
// const { count } = storeToRefs(counterStore)
</script>
pinia模块化
在复杂项目中,不可能吧多个模块的数据都定义到一个store中,一般来说会一个模块对应一个store,最后通过一个根store进行整合
(1)新建store/user.js文件
import { defineStore } from 'pinia'
// vue2 模块化
// this.$store.user.state
// this.$store.counter.state
const useUserStore = defineStore('user', {
state: () => {
return {
name: 'zs',
age: 100,
}
},
})
export default useUserStore
(2)新建store/index.js
import useCounterStore from "./modules/counter";
import useUserStore from "./modules/user";
// 写在外面,加载完就不会再调用了,是一次性的
// const counter = useCounterStore();
// const user = useUserStore();
// 通过属性调用,每次都保证拿到最新的 store
const useStore = () => {
return {
counter: useCounterStore(),
user: useUserStore(),
};
};
export default useStore;
(3)在组件中使用
<script setup>
import { toRefs } from 'vue';
import useStore from './store'
const { user, counter } = useStore()
const { double } = toRefs(counter)
</script>
<template>
<!-- 所有的方法都测一下 -->
<h1>Hello vue3</h1>
<div>{{ user }}</div>
<div>{{ counter }}</div>
<div>{{ user.name }}</div>
<div>{{ counter.count }}</div>
<button @click="counter.add">同步+1</button>
<button @click="counter.addNum(10)">+10</button>
<button @click="counter.addAsync">异步加1</button>
<div>{{ double }}</div>
</template>
<style scoped>
</style>
\