目录
认识pinia
pinia是一个全局状态管理工具
Pinia.js具有以下特点
- 完整的 ts 的支持;
- 足够轻量,压缩后的体积只有1kb左右;
- 去除 mutations,只有 state,getters,actions;
- actions 支持同步和异步;
- 代码扁平化没有模块嵌套,只有 store 的概念,store 之间可以自由使用,每一个store都是独立的
- 无需手动添加 store,store 一旦创建便会自动添加;
- 支持Vue3 和 Vue2
1. 起步 安装
yarn add pinia
npm install pinia
2. 引入注册
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import {createPinia} from "pinia";
const pinia = createPinia()
const app = createApp(App)
app.use(ElementPlus)
app.use(pinia)
app.mount('#app')
3. 初始化仓库store
需要以下几步
1.新建一个文件夹Store
2.新建文件[name].ts
3.定义仓库Store
import { defineStore } from 'pinia'
4.存储是使用定义的defineStore()
,并且它需要一个唯一的名称,作为第一个参数传递
我们定义了一个枚举去做这个唯一值
5.定义值
State 箭头函数 返回一个对象 在对象里面定义值
getters 类似于computed
actions 可以提交一些同步或异步的方法来改变state
import {defineStore} from "pinia";
import {Namespace} from "./namespace.ts";
export const useStore = defineStore(Namespace.TEST, {
state: () => {
return {
count: 0
}
},
getters: {
},
actions: {
}
});
使用pinia---state
在vue中需要调用导出的pinia 调用这个useStore函数,拿到他的返回值对象即可进行使用
<script setup lang='ts'>
import {useStore} from "./store";
const Test = useStore()
console.log(Test.count)
</script>
<template>
</template>
<style scoped>
</style>
修改state值的方式
1. 直接修改
<script setup lang='ts'>
import {useStore} from "./store";
const Test = useStore()
</script>
<template>
<button @click="Test.count++">增加</button>
{{Test.count}}----{{Test.name}}
</template>
<style scoped>
</style>
2. 批量修改state的值
在他的实例上有$patch方法可以批量修改多个值
$patch传入一个对象,进行修改
<script setup lang='ts'>
import {useStore} from "./store";
const Test = useStore()
function change() {
Test.$patch({
count: 10,
name: '张三'
})
}
</script>
<template>
<button @click="Test.count++">增加</button>
<button @click="change">修改</button>
{{Test.count}}----{{Test.name}}
</template>
<style scoped>
</style>
3. 批量修改函数形式
$patch传入一个回调,进行修改,回调上面有一个参数state,公告state去修改,主要的目的就是为了带一下条件的去修改值
<script setup lang='ts'>
import {useStore} from "./store";
const Test = useStore()
function change() {
Test.$patch((state) => {
if(state.count < 10) {
state.count = 10
}
})
}
</script>
<template>
<button @click="Test.count++">增加</button>
<button @click="change">修改</button>
{{Test.count}}----{{Test.name}}
</template>
<style scoped>
</style>
4. 通过原始对象修改整个实例
$state
您可以通过将store的属性设置为新对象来替换store的整个状态
缺点就是必须修改整个对象的所有属性
<script setup lang='ts'>
import {useStore} from "./store";
const Test = useStore()
function change() {
Test.$state = {
count: 10,
name: '张三'
}
}
</script>
<template>
<button @click="Test.count++">增加</button>
<button @click="change">修改</button>
{{Test.count}}----{{Test.name}}
</template>
<style scoped>
</style>
5. 通过actions来进行修改
import {defineStore} from "pinia";
import {Namespace} from "./namespace.ts";
export const useStore = defineStore(Namespace.TEST, {
state: () => {
return {
count: 0,
name: 'jjs'
}
},
getters: {
},
actions: {
changeState(num:number) {
this.count += num;
}
}
});
<script setup lang='ts'>
import {useStore} from "./store";
const Test = useStore()
function change() {
Test.$state = {
count: 10,
name: '张三'
}
}
function add(num:number) {
Test.changeState(num)
}
</script>
<template>
<button @click="add(10)">增加</button>
<button @click="change">修改</button>
{{Test.count}}----{{Test.name}}
</template>
<style scoped>
</style>
6. 解构state
let {count,name} = Test.$state
像这样直接去结构的话,就会失去响应式。数据就不是响应式的了
解决方案可以使用 storeToRefs
storeToRefs原理: 像使用toRaw将proxy对象转换为原对象,然后通过循环给属性都调用toRefs方法将属性都转为ref对象,这样解构出来的属性值就是响应式的了。
<script setup lang='ts'>
import {useStore} from "./store";
import {storeToRefs} from "pinia";
const Test = useStore()
let {count,name} = storeToRefs(Test)
function change() {
Test.$state = {
count: 10,
name: '张三'
}
}
function add(num:number) {
Test.changeState(num)
}
</script>
<template>
<div>
<button @click="add(10)">增加</button>
<button @click="change">修改</button>
</div>
<p>{{Test.count}}----{{Test.name}}</p>
<p>{{name}}----{{count}}</p>
</template>
<style scoped>
</style>
使用pinia---getters,actions
1. actions
可以支持同步,异步和相互调用
下面有一个案例来说明:
import {defineStore} from "pinia";
import {Namespace} from "./namespace.ts";
type User = {
name: string,
age: number
}
const Login = ():Promise<User> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: 'jjs',
age: 18
})
},2000)
})
}
export const useStore = defineStore(Namespace.TEST, {
state: () => {
return {
count: 0,
name: 'jjs',
user: <User>{}
}
},
getters: {
},
actions: {
changeStateCount(num:number) {
this.count += num;
},
changeName(){
this.name = 'jjs2'
},
async login() {
let result = await Login();
console.log(result)
this.user = result;
this.changeName()
}
}
});
<script setup lang='ts'>
import {useStore} from "./store";
const store = useStore();
store.login()
</script>
<template>
<p>{{store.user}}</p>
<p>{{store.name}}</p>
</template>
<style scoped>
</style>
2. getters
getters: {
newName(state) {
return state.name + 'new'
},
doubleCount: (state) => {
return state.count + state.newName
}
},
<script setup lang='ts'>
import {useStore} from "./store";
const store = useStore();
store.login()
</script>
<template>
<p>{{store.user}}</p>
<p>{{store.name}}</p>
<p>{{store.doubleCount}}</p>
<button @click="store.count++">++</button>
</template>
<style scoped>
</style>
pinia的一些api
1. $reset
重置state到初始状态
<script setup lang='ts'>
import {useStore} from "./store";
const store = useStore();
store.login()
</script>
<template>
<p>{{store.user}}</p>
<p>{{store.name}}</p>
<p>{{store.doubleCount}}</p>
<button @click="store.count++">++</button>
<div>
<button @click="store.$reset()">重置</button>
</div>
</template>
<style scoped>
</style>
2. 订阅state的改变
类似于Vuex 的abscribe 只要有state 的变化就会走这个函数
store.$subscribe((state, getters) => {
console.log(state, getters)
})
3.订阅Actions的调用
只要有actions被调用就会走这个函数
store.$onAction(({name, store, after, onError}) => {
console.log(name, store, after, onError)
})
持久化存储
pinia 和 vuex 都有一个通病 页面刷新状态会丢失
我们可以写一个pinia 插件缓存他的值
const __piniaKey = '__PINIAKEY__'
//定义兜底变量
type Options = {
key?:string
}
//定义入参类型
//将数据存在本地
const setStorage = (key: string, value: any): void => {
localStorage.setItem(key, JSON.stringify(value))
}
//存缓存中读取
const getStorage = (key: string) => {
return (localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key) as string) : {})
}
//利用函数柯丽华接受用户入参
const piniaPlugin = (options: Options) => {
//将函数返回给pinia 让pinia 调用 注入 context
return (context: PiniaPluginContext) => {
const { store } = context;
const data = getStorage(`${options?.key ?? __piniaKey}-${store.$id}`)
store.$subscribe(() => {
setStorage(`${options?.key ?? __piniaKey}-${store.$id}`, toRaw(store.$state));
})
//返回值覆盖pinia 原始值
return {
...data
}
}
}
//初始化pinia
const pinia = createPinia()
//注册pinia 插件
pinia.use(piniaPlugin({
key: "pinia"
}))