vue3-pinia

391 阅读5分钟

Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态

1.0 安装

npm install pinia

2.0 定义 Store

2.1 什么是store

Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念stategetteraction,我们可以假设这些概念相当于组件中的 datacomputedmethods

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>

清醒温柔知进退 ,努力上进且优秀。

qdysh.png