Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态
1.0 安装
npm install pinia
2.0 定义 Store
2.1 什么是store
Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念,state、getter 和 action,我们可以假设这些概念相当于组件中的
data、computed和methods。
2.2定义store
Store 是用 defineStore() 定义的,它的第一个参数要求是一个独一无二的名字:
import { defineStore } from "pinia";
// 创建一个store并暴露出去
// 参数1 名称,保证唯一
// 参数2 对象形式(选项式的)
export const useMainStore = defineStore('main', {
state: () => ({count: 0}), // 共享的数据
getters: {}, // 通过计算得到的共享数据
actions:{} // 共享的函数
})
/*
* 你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),
而 actions 则是方法 (methods)。
* */
// 参数2 函数形式 (组合式)
// export const useMainStore = defineStore('main',()=>{
// ref 变量 共享的数据
// computed() 通过计算得到的共享数据
// function
// return {
// 返回组件需要的变量,计算属性,函数
// }
// })
修改main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
// 创建并应用到整个应用中
app.use(createPinia())
app.use(router)
app.mount('#app')
选项式取数据
新建文件stores/useUserStore.js
import { defineStore } from "pinia";
export const useUserStore = defineStore('user', {
// 共享的数据
state: () => ({
age:27,
level:5,
account:123456,
nickname:'小红'
}),
getters: {}, // 通过计算得到的共享数据
actions:{} // 共享的函数
})
在任意组件接收mapState
<template>
<h2>年龄: {{age}}</h2>
<h3>等级: {{level}}</h3>
<h3>账号: {{user_account}}</h3>
<h3>昵称: {{user_nickname}}</h3>
<button @click="addNumber">点我增加</button>
</template>
<script >
import { mapState } from 'pinia'
import { useUserStore } from "../stores/useUserStore";
export default {
computed:{
// 属性时只读的不能修改
// 1.0 数组形式
...mapState(useUserStore,['age','level']),
// 2.0 对象形式 别名
...mapState(useUserStore,{
user_account: 'account',
user_nickname: 'nickname',
})
}
}
</script>
当然也可以变成可修改的mapWritableState
<template>
<h1>mapState映射的数据</h1>
<h2>年龄: {{age}}</h2>
<h3>等级: {{level}}</h3>
<h3>账号: {{user_account}}</h3>
<h3>昵称: {{user_nickname}}</h3>
<hr>
<h1>mapWritableState映射的数据</h1>
<h3>账号: {{account}}</h3>
<h3>昵称: {{nickname}}</h3>
<h2>年龄: {{user_age}}</h2>
<h3>等级: {{user_level}}</h3>
<button @click="nickname += '大大'">点修改昵称</button>
<button @click="account += '1111'">点修改账号</button>
<button @click="user_age += 2">点修改年龄</button>
<button @click="user_level += '1'">点修改等级</button>
</template>
<script >
import { mapState,mapWritableState } from 'pinia'
import { useUserStore } from "../stores/useUserStore";
export default {
computed:{
// 属性时只读的不能修改
// 1.0 数组形式
...mapState(useUserStore,['age','level']),
// 2.0 对象形式 别名
...mapState(useUserStore,{
user_account: 'account',
user_nickname: 'nickname',
}),
// 3.0 数组形式
...mapWritableState(useUserStore,['account','nickname']),
// 4.0 对象形式 别名
...mapWritableState(useUserStore,{
user_age:'age',
user_level:'level'
})
}
}
</script>
组合式取数据
新建文件stores/useUserStore2.js
import { defineStore } from "pinia";
import { ref } from 'vue'
// 组合式的
export const useUserStore2 = defineStore('user2', ()=>{
let age = ref(27)
let level = ref(5)
let account = ref('123456')
let nickname = ref('小红')
return { // 变量共享出去
age,
level,
account,
nickname,
}
})
组件取值并使用
<template>
<h1>从store直接取 state</h1>
<h2>年龄: {{user_store.age}}</h2>
<h3>等级: {{user_store.level}}</h3>
<h3>账号: {{user_store.account}}</h3>
<h3>昵称: {{user_store.nickname}}</h3>
<hr>
<button @click="user_store.nickname += '大大'">点修改昵称</button>
<button @click="user_store.account += '1111'">点修改账号</button>
<button @click="user_store.age += 2">点修改年龄</button>
<button @click="user_store.level += '1'">点修改等级</button>
<hr>
<h1>computed映射为计算属性</h1>
<h2>年龄: {{user_age}}</h2>
<button @click="user_age += 2">点修改年龄</button>
<hr>
<h1>storeToRefs解构响应式的store</h1>
<h2>年龄: {{age}}</h2>
<h3>等级: {{level}}</h3>
<h3>账号: {{z_account}}</h3>
<h3>昵称: {{z_nickname}}</h3>
<button @click="z_nickname += '大大'">点修改昵称</button>
<button @click="z_account += '1111'">点修改账号</button>
<button @click="age += 2">点修改年龄</button>
<button @click="level += '1'">点修改等级</button>
<hr>
<h1>$patch 替换多个状态值</h1>
<button @click="changeMuchData">点我同时替换多个值</button>
<button @click="changeResetData">点我重置数据</button>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useUserStore2 } from "../stores/useUserStore2";
import {computed} from "vue";
// 1.0 获取对象实例 这种是不可以修改的
let user_store = useUserStore2()
// 2.0 这种是不可以修改的
let user_age = computed(()=> user_store.age )
// 3.0 解构响应式的数据
let {
age,
level,
account: z_account,// 别名
nickname: z_nickname,
} = storeToRefs(user_store)
// 点击同时换多个值
// 可以调用 $patch 方法。它允许你用一个 state 的补丁对象在同一时间更改多个属性:
let changeMuchData = ()=>{
user_store.$patch({
age:15,
nickname:'小亮'
})
}
let changeResetData = () =>{
user_store.$reset()
}
/*
* 报错了 Uncaught Error: 🍍: Store "user2" is built using the setup syntax and does not implement $reset().
* */
</script>
这个 $reset 在组合式的时候会报错,所以要么你再定义store的时候用选项式不用组合式,如果非要定义的时候用组合式,用的时候也用到了它,那我们就自定义一个方法
修改main.js重写一个$reset方法
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
// 创建并应用到整个应用中
const pinia = createPinia()
pinia.use(({ store }) => {
const initialState = JSON.parse(JSON.stringify(store.$state));
store.$reset = () => {
store.$patch(initialState);
}
})
app.use(pinia)
app.use(router)
app.mount('#app')
3.0 定义getters
3.1选项式
新建文件stores/useInfoStore.js定义数据
import { defineStore } from "pinia";
// 选项式的
export const useInfoStore = defineStore('info', {
// 共享的数据
state: () => ({
age:27,
birth:'1991-02-15'
}),
// 通过计算得到的共享数据 只读
// 如果依赖的数据发生变化,则会重新计算
getters: {
infoMonth(){
// this.是store实例
return this.birth.split('-')[1]
},
// 参数1 是 store实例
ageState:(store) =>{
if(store.age > 18){
return '成年人';
}else{
return '未成年';
}
}
},
actions:{} // 共享的函数
})
新建文件取数据
<template>
<h1>mapState映射的getters的数据也不具备响应式</h1>
<h2>年龄: {{age}}</h2>
<h3>生日: {{birth}}</h3>
<h3>getters的月份:{{infoMonth}}</h3>
<h3>年龄阶段:{{s_ageState}}</h3>
<button @click="age = 16">点修改年龄</button>
<hr>
<h1>mapWritableState映射的getters的数据也不具备响应式</h1>
<h3>getters的月份:{{z_infoMonth}}</h3>
<h3>年龄阶段:{{ageState}}</h3>
<button @click="z_infoMonth = 5">点修改月份</button>
<button @click="ageState = '1111'">点修改年龄</button>
</template>
<script >
import { mapState,mapWritableState } from 'pinia'
import { useInfoStore } from "../stores/useInfoStore";
export default {
computed:{
// 从store 取getters和去state用法一样,都可以用mapstate
// 1.0 数组形式
...mapState(useInfoStore,['birth','infoMonth']),
// 2.0 对象形式 别名
...mapState(useInfoStore,{
s_ageState: 'ageState',
}),
// 3.0 数组形式
...mapWritableState(useInfoStore,['ageState','age']),
// 4.0 对象形式 别名
...mapWritableState(useInfoStore,{
z_infoMonth: 'infoMonth',
})
}
}
</script>
3.1组合数据
新建文件stores/useInfoStore2.js定义数据
import { defineStore } from "pinia";
import {computed, ref} from 'vue'
// 组合式的
export const useInfoStore2 = defineStore('info2', ()=>{
let birth = ref('1992-02-16')
let age = ref(30)
// 通过computed 获得计算的属性 只读 通过依赖的数据改变
let birthMonth = computed(()=>{
return birth.value.split('-')[1]
})
let ageState = computed(()=>{
if(age.value > 18){
return '成年人';
}else{
return '未成年';
}
})
return {birth,age,birthMonth,ageState}
})
新建文件取值
<template>
<h1>从store直接取 getters</h1>
<h3>月份: {{info_store.birthMonth}}</h3>
<h3>年龄状态: {{info_store.ageState}}</h3>
<hr>
<!-- <button @click="user_store.nickname += '大大'">点修改昵称</button>-->
<!-- <button @click="user_store.account += '1111'">点修改账号</button>-->
<!-- <button @click="user_store.age += 2">点修改年龄</button>-->
<!-- <button @click="user_store.level += '1'">点修改等级</button>-->
<!-- <hr>-->
<h1>computed映射为计算属性取getters</h1>
<h3>月份: {{bir_birthMonth}}</h3>
<h3>年龄状态: {{bir_ageState}}</h3>
<button @click="bir_birthMonth += 2">点修改年龄</button>
<hr>
<h1>storeToRefs解构响应式的getters为自己的属性</h1>
<h2>生日: {{birth}}</h2>
<h3>年龄: {{age}}</h3>
<h3>月份: {{z_birthMonth}}</h3>
<h3>年龄状态: {{ageState}}</h3>
<button @click="birth = '1992-11-11'">点修改生日</button>
<button @click="age -= 5">点修改年龄</button>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useInfoStore2 } from "../stores/useInfoStore2";
import {computed} from "vue";
// 1.0 获取对象实例 这种是不可以修改的
let info_store = useInfoStore2()
// 2.0 这种是不可以修改的
let bir_birthMonth= computed(()=> info_store.birthMonth )
let bir_ageState= computed(()=> info_store.ageState )
// 3.0 解构响应式的数据
let {
age,
birth,
birthMonth: z_birthMonth,// 别名
ageState
} = storeToRefs(info_store)
</script>
4.0 定义actions
actions 一般是对state中的数据进行修改的业务逻辑函数,actions也可以是异步的,可以再其中 await 任何的api调用甚至其他操作
4.1选项式
新增文件stores/usePeopleStore.js
import { defineStore } from "pinia";
// 选项式的
export const usePeopleStore = defineStore('people', {
// 共享的数据
state: () => ({
age:27,
name:'小红'
}),
// 共享的函数 主要作用修改state里面的数据 可以是异步
actions:{
// 修改名字和名字
setPeopleInfo(name,age){
// this当前的 store的实例对象
this.name = name;
this.age = age;
},
setPeopleInfoObj(people){
this.name = people.name;
this.age = people.age;
}
}
})
修改数据页面
<template>
<h1>mapState映射的数据也不具备响应式</h1>
<h2>年龄: {{age}}</h2>
<h3>名字: {{name}}</h3>
<button @click="setPeopleInfo('问问','18')">点我改变people</button>
<button @click="z_setPeopleInfoObj({name:'文文',age:19})">别名修改people</button>
</template>
<script >
import {mapActions, mapState} from 'pinia'
import { usePeopleStore } from "../stores/usePeopleStore";
export default {
computed:{
...mapState(usePeopleStore,['age','name'])
},
methods:{
// mapActions映射为自己的函数
// 1.0 字符串模式
...mapActions(usePeopleStore,['setPeopleInfo']),
// 2.0 对象模式 可以用别名
...mapActions(usePeopleStore,{
z_setPeopleInfoObj:'setPeopleInfoObj'
})
}
}
</script>
4.2组合式
新增文件stores/usePeopleStore2.js
import { defineStore } from "pinia";
import {ref} from "vue";
// 组合式的
export const usePeopleStore2 = defineStore('people2', ()=>{
let age = ref(27)
let name = ref('小红')
function setPeopleInfo(n,a){
name.value = n;
age.value = a;
}
function setPeopleInfoObj(people){
name.value = people.name;
age.value = people.age;
}
return {
age,name,setPeopleInfo,setPeopleInfoObj
}
})
修改数据页面
<template>
<h1>通过store对象直接调用</h1>
<h2>年龄: {{age}}</h2>
<h3>名字: {{name}}</h3>
<button @click="people_store.setPeopleInfo('问问','18')">点我改变people1</button>
<button @click="people_store.setPeopleInfoObj({name:'文文',age:19})">点我改变people2</button>
<hr>
<h1>storeToRefs映射为本地函数</h1>
<button @click="setPeopleInfo('红红','20')">函数改变</button>
<button @click="z_setPeopleInfoObj({name:'红红2',age:'202'})">函数别名改变</button>
</template>
<script setup>
import { storeToRefs} from 'pinia'
import { usePeopleStore2 } from "../stores/usePeopleStore2";
// 1.0 获取对象实例 这种是不可以修改的
let people_store = usePeopleStore2()
// 解构出来
let {age,name} = storeToRefs(people_store)
// 方法需要解构
let {
setPeopleInfo,
setPeopleInfoObj:z_setPeopleInfoObj,// 别名
} = people_store
</script>
清醒温柔知进退 ,努力上进且优秀。