pinia的使用
pinia的使用
pinia介绍
Pinia 是一个状态管理库,由 Vue 核心团队维护,对 Vue 2 和 Vue 3 都可用。
现有用户可能对 Vuex 更熟悉,它是 Vue 之前的官方状态管理库。由于 Pinia 在生态系统中能够承担相同的职责且能做得更好,因此 Vuex 现在处于维护模式。它仍然可以工作,但不再接受新的功能。对于新的应用程序,建议使用 Pinia。
事实上,Pinia 这款产品最初是为了探索 Vuex 的下一个版本,整合了核心团队关于 Vuex 5 的许多想法。最终,我们意识到 Pinia 已经实现了我们想要在 Vuex 5 中提供的大部分内容,因此决定将其作为新的官方推荐。
相比于 Vuex,Pinia提供了更简洁直接的 API,并提供了组合式风格的 API,最重要的是,在使用 TypeScript 时它提供了非常好的类型推导。
pinia特点
Pinia.js 有如下特点:
完整的 ts 的支持; 足够轻量,压缩后的体积只有1kb左右; 去除 mutations,只有 state,getters,actions; actions 支持同步和异步; 代码扁平化没有模块嵌套,只有 store 的概念,store 之间可以自由使用,每一个store都是独立的 无需手动添加 store,store 一旦创建便会自动添加; 支持Vue3 和 Vue2
官方文档 Pinia
git 地址 [https://github.com/vuejs/pinia](
安装
yarn add pinia
npm install pinia
main文件引入
vue3 main.ts引入
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
import './assets/global.css'
createApp(App).use(createPinia()).mount('#app')
vue2 main.js引入
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// other options...
// ...
// note the same `pinia` instance can be used across multiple Vue apps on
// the same page
pinia,
})
新建Store文件夹
定义仓库Store
import { defineStore } from 'pinia'
我们需要知道存储是使用定义的defineStore(),并且它需要一个唯一的名称,作为第一个参数传递
我这儿名称抽离出去了
在store文件夹中新建name.ts文件
export const enum Names {
Test = 'TEST'
}
store/index.ts引入
import { defineStore } from 'pinia'
import { Names } from './store-namespace'
export const useTestStore = defineStore(Names.Test, {
})
State 箭头函数 返回一个对象 在对象里面定义值
import { defineStore } from 'pinia'
import { Names } from './name'
export const useTestStore = defineStore(Names.Test, {
state: () => {
return {
current: 1,
age: 11
}
},
//类似于computed 可以帮我们去修饰我们的值
getters: {
},
//可以操作异步 和 同步提交state
actions: {
}
})
页面中使用
<!-- App.vue -->
<template>
<div>
<button @click="Add">+</button>
<div>
{{ count.current }}
</div>
<div>
{{ count.age }}
</div>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const count = useTestStore()
const Add = () => {
count.current++
}
</script>
<style>
</style>

在他的实例上有$patch方法可以批量修改多个值
<!-- App.vue -->
<template>
<div>
<button @click="Add">+</button>
<div>
{{ count.current }}
</div>
<div>
{{ count.age }}
</div>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const count = useTestStore()
const Add = () => {
count.$patch({
current: 200,
age: 300
})
}
</script>
<style>
</style>

推荐使用函数形式 可以自定义修改逻辑
<!-- App.vue -->
<template>
<div>
<button @click="Add">+</button>
<div>
{{ count.current }}
</div>
<div>
{{ count.age }}
</div>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const count = useTestStore()
const Add = () => {
count.$patch((state) => {
state.current++;
state.age = 40
})
}
</script>
<style>
</style>

$state您可以通过将store的属性设置为新对象来替换store的整个状态
缺点就是必须修改整个对象的所有属性
<!-- App.vue -->
<template>
<div>
<button @click="Add">+</button>
<div>
{{ count.current }}
</div>
<div>
{{ count.age }}
</div>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const count = useTestStore()
const Add = () => {
count.$state = {
current: 10,
age: 30
}
}
</script>
<style>
</style>

定义Actions
在actions 中直接使用this就可以指到state里面的值
// store/index.ts
import { defineStore } from 'pinia'
import { Names } from './name'
export const useTestStore = defineStore(Names.Test, {
state: () => {
return {
current: 1,
age: 11
}
},
//类似于computed 可以帮我们去修饰我们的值
getters: {
},
//可以操作异步 和 同步提交state
actions: {
setCurrent() {
this.current++
this.age++
}
}
})
使用方法直接在实例调用
<!-- App.vue -->
<template>
<div>
<button @click="Add">+</button>
<div>
{{ count.current }}
</div>
<div>
{{ count.age }}
</div>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const count = useTestStore()
const Add = () => {
count.setCurrent()
}
</script>
<style>
</style>

解构store
在Pinia是不允许直接解构是会失去响应性的
const Test = useTestStore()
const { current, name } = Test
console.log(current, name);
差异对比
修改Test current 解构完之后的数据不会变
而源数据是会变的
<!-- App.vue -->
<template>
<div>origin value {
{cont.current}}</div>
<div>
pinia:{{ current }}--{{ age }}
change :
<button @click="change">change</button>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const cont = useTestStore()
const change = () => {
cont.current++
}
const { current, age } = cont
console.log(current, age);
</script>
<style>
</style>
解决方案可以使用storeToRefs
<!-- App.vue -->
import { storeToRefs } from 'pinia'
const Test = useTestStore()
const { current, name } = storeToRefs(Test)
其原理跟toRefs 一样的给里面的数据包裹一层toref
源码 通过toRaw使store变回原始数据防止重复代理
循环store 通过 isRef isReactive 判断 如果是响应式对象直接拷贝一份给refs 对象 将其原始对象包裹toRef 使其变为响应式对象
Actions,getters
Actions
1.同步 直接调用即可
// store/index.ts
import { defineStore } from 'pinia'
import { Names } from './name'
export const useTestStore = defineStore(Names.Test, {
state: () => {
return {
current: 0
}
},
//类似于computed 可以帮我们去修饰我们的值
getters: {
},
//可以操作异步 和 同步提交state
actions: {
randomizeCounter() {
this.current = Math.round(100 * Math.random())
},
}
})
<!-- App.vue -->
<template>
<div>
<button @click="Add">+</button>
<div>
{{ count.current }}
</div>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const count = useTestStore()
const Add = () => {
count.randomizeCounter()
}
</script>
<style>
</style>

2.异步 可以结合async await 修饰
// store/index.ts
import { defineStore } from 'pinia'
import { Names } from './name'
type Result = {
name: string
isChu: boolean
}
const Login = (): Promise<Result> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: '小满',
isChu: true
})
}, 3000)
})
}
export const useTestStore = defineStore(Names.Test, {
state: () => ({
user: <Result>{},
name: "123"
}),
//类似于computed 可以帮我们去修饰我们的值
getters: {
},
//可以操作异步 和 同步提交state
actions: {
async getLoginInfo() {
const result = await Login()
this.user = result;
}
}
})
<!-- App.vue -->
<template>
<div>
<button @click="Add">test</button>
<div>
{{ count.user }}
</div>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const count = useTestStore()
const Add = () => {
count.getLoginInfo()
}
</script>
<style>
</style>

3.多个action互相调用getLoginInfo setName
state: () => ({
user: <Result>{},
name: "default"
}),
actions: {
async getLoginInfo() {
const result = await Login()
this.user = result;
this.setName(result.name)
},
setName (name:string) {
this.name = name;
}
},
getters
1.使用箭头函数不能使用this this指向已经改变指向undefined 修改值请用state
主要作用类似于computed 数据修饰并且有缓存
getters:{
newPrice:(state)=> `$${state.user.price}`
},
2.普通函数形式可以使用this
getters:{
newCurrent ():number {
return ++this.current
}
},
3.getters 互相调用
getters:{
newCurrent ():number | string {
return ++this.current + this.newName
},
newName ():string {
return `$-${this.name}`
}
},
API
重置store到他的初始状态
state: () => ({
user: <Result>{},
name: "default",
current:1
}),
Vue 例如我把值改变到了10
const change = () => {
count.current++
}
调用$reset();
将会把state所有值 重置回 原始状态
类似于Vuex 的abscribe 只要有state 的变化就会走这个函数
count.$subscribe((args,state)=>{
console.log(args,state);
})
第二个参数
如果你的组件卸载之后还想继续调用请设置第二个参数
count.$subscribe((args,state)=>{
console.log(args,state);
},{
detached:true
})
只要有actions被调用就会走这个函数
count.$onAction((args)=>{
console.log(args);
})
存储状态并持久化存储
pinia 和 vuex 都有一个通病 页面刷新状态会丢失
安装 pinia-persistedstate-plugin
npm install pinia-persistedstate-plugin
main.ts文件修改
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
import { createPersistedState } from "pinia-persistedstate-plugin";
const store = createPinia();
store.use(createPersistedState());
createApp(App).use(store).mount('#app')
然后使用pinia刷新页面就不会出现状态消失了