状态机配置
一、全局挂载 pinia
在 main.ts
中全局挂载 pinia 对象:
import { createPinia } from 'pinia'
const pinia = createPinia()
const app = createApp(App)
// ...
app.use(pinia);
app.mount('#app');
二、store 的基本配置
import { defineStore } from 'pinia'
const useRolesStore = defineStore('roles', {
// 定义公共数据
state: () => ({}),
// 定义公共计算属性
getters: {},
// 定义公共的方法(同步和异步)
actions: {}
});
export default useRolesStore;
三、state 和 action 的使用
state:用来存储数据,接收的是一个箭头函数的返回值,不能直接接收一个对象
const useRolesStore = defineStore('roles', {
//state类似于组件的data,用来存储全局状态的
//state必须是函数:这样是为了在服务端渲染的时候避免交叉请求导致的数据状态污染
//必须是箭头函数,这是为了TS更好的类型推导
state: () => ({
rolesData: []
}),
getters: {},
actions: {
async getRolesAsync() {
const res: any = await getRolesApi();
if (res.code) {
// 将请求结果保存到 state
this.rolesData = res.data;
}
}
}
});
四、组件调用 action 方法
import useRolesStore from '../../../store/roles';
const rolesStore = useRolesStore();
// ...
//注意不能使用箭头函数定义actions:因为箭头函数绑定外部this,会改变this指向
const getRoles = async () => {
// 调用仓库的方法
rolesStore.getRolesAsync();
// 同理,actions里也可使用$patch
this.$patch({})
this.$patch(state=>{})
//在此注意:patch和普通多次修改的区别在原理上的区别是
// 1.涉及到数据响应式和视图更新,多次修改,修改几次视图就更新就更新几次
// 2.patch 批量修改 视图只更新一次,更有利于性能优化
}
五、组件获取 state 数据
import useRolesStore from '../../../store/roles';
const rolesStore = useRolesStore();
// 通过计算属性接收仓库的 state
const rolesData = computed(() => {
return rolesStore.rolesData;
})
六、getter是store的计算属性
-
类似于vue的计算属性
-
可以在getter方法中使用另外的getter方法,通过
this
使用 -
在页面中使用getter时,可以传递一个参数进去
export const useMapStore = defineStore("map", { state: () => { return { name: "小猪课堂", age: 25, sex: "男", }; }, getters: { //箭头函数写法, getAddAge: (state) => { //console.log(this); //使用箭头函数,this无效,可以通过 state.其他getters的名称 来访问别的getters return state.age + 100; }, //箭头函数,接收参数 getNewAge: (state) => { return (params) => `${state.name},q技能叫做${params}` } // 调用其它getter getNameAndAge(): string { //普通函数,this与state的值是一样的,可以通过this/state.其他getters的名称/state中其他参数名进行调用 return this.name + this.getAddAge; // 调用其它getter }, //在页面中使用getter时,可以传递一个参数进去,通过回调函数接收 getNewAddAge: (state) => { return (num: number) => state.age + num; }, },
});
4. 在任意页面组件中
```ts
<p>调用其它getter:{{ mapStore.getNameAndAge }}</p>
<p>向getter传递参数,新年龄:{{ store.getNewAddAge(1100) }}</p>
<script>
import {computed} from 'vue';
import useMapStore from "@/stores/map";
const mapStore = useMapStore();
const nameAndAge = computed(() => mapStore.getNameAndAge);
const newAge = computed(() => mapStore.getNewAge('位移'))
</script>
七、利用插件pinia-plugin-persist持久化
enabled
:开启后,默认会对整个store
的state
内容进行储存
key
:自定义存储的key,默认是store.$id
的值
storage
:可以指定任何 extends Storage
的实例,默认是sessionStorage
paths
:state中的字段名,按组打包储存
第一步:安装插件pinia-plugin-persist
npm install pinia-plugin-persist --save
第二步:修改store文件夹下的index.ts
import { createPinia } from "pinia"
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPluginPersist)
export default store
第三步:修改user.ts,为其添加persist
import {defineStore} from "pinia"
export const useUserStore = defineStore({
id: "user",
state: () => {
return {
name: '',
age: 0
}
},
persist: {
enabled: true,
strategies: [
{
key: 'wego_user',
storage: localStorage,
//通过在strategies中可以指定对哪些数据进行缓存
paths: ['token', 'name']
}
]
}
})
storage
属性可以是任何继承自Storage
协议的对象,自定义存储对象也可以
cookiesStorage
举例:
import Cookies from 'js-cookie'
const cookiesStorage: Storage = {
setItem(key, state){
return Cookies.set('accessToken', state.accessToken, {expires: 3})
},
getItem(key){
return JSON.stringify({
accessToken: Cookies.getJSON('accessToken'),
})
}
}
export const useStore = defineStore("YourStore", () => {
const foo = ref("foo")
const bar = ref("bar")
const accessToken = ref("xxx")
return {foo, bar, accessToken}
}, {
enabled: true,
strategies: [{
key: 'token',
storage: cookiesStorage,
paths: ["accessToken"]
}]
})
源码解读
type Store = PiniaPluginContext['store'];
type PartialState = Partial<Store['$state']>;
export const updateStorage = (strategy: PersistStrategy, store: Store) => {
// 默认使用 sessionStorage
const storage = strategy.storage || sessionStorage
// 默认存储 key 为 store.$id
const storeKey = strategy.key || store.$id
if (strategy.paths) {
// 遍历 paths 将对应的属性收集到 finalObj 中
const partialState = strategy.paths.reduce((finalObj, key) => {
finalObj[key] = store.$state[key]
return finalObj
}, {} as PartialState)
// 执行存储
storage.setItem(storeKey, JSON.stringify(partialState))
} else {
// 如果没有 paths,则按整个 store.$state 存储
storage.setItem(storeKey, JSON.stringify(store.$state))
}
}
export default ({ options, store }: PiniaPluginContext): void => {
// 判断插件功能是否开启
if (options.persist?.enabled) {
// 默认策略实例
const defaultStrat: PersistStrategy[] = [{
key: store.$id,
storage: sessionStorage,
}]
const strategies = options.persist?.strategies?.length ? options.persist?.strategies : defaultStrat
strategies.forEach((strategy) => {
const storage = strategy.storage || sessionStorage
const storeKey = strategy.key || store.$id
const storageResult = storage.getItem(storeKey)
if (storageResult) {
// 如果 storage 中存在同步数据
store.$patch(JSON.parse(storageResult))
updateStorage(strategy, store)
}
})
store.$subscribe(() => {
// 监听 state 变化,同步更新 storage
strategies.forEach((strategy) => {
updateStorage(strategy, store)
})
})
}
}
API
storeToRefs()
:解构state中的数据,并成为响应式数据
import useMapStore from "@/stores/map";
import { storeToRefs } from 'pinia';
const mapStore = useMapStore();
const { name, age } = storeToRefs(mapStore);
store.$reset()
:重置仓库数据
使用场景:用户填了一部分表单,想重置为最初始数据
<button @click="reset">重置</button>
const reset = () => {
mapStore.$reset();
}
store.$patch()
:批量更改数据
可以传递一个对象,也可以传递一个回调函数,参数为仓库中的state,可以实现一部分修改
<button @click="patchStore">批量修改数据</button>
const patchStore = () => {
mapStore.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
}
store.$subscribe
对state订阅监听
import {defineStore} from "pinia"
const appStore = defineStore('appStore', {
state: () => ({
baseUrl: 'https://www.baidu.com/'
}),
actions: {
changeState(params) {
// console.log('接收到的参数===>', params)
this.baseUrl = params
}
}
})
export default appStore
<template>
<div>
{{ baseUrl }}
</div>
<div v-show="isShow">
该我出现了
</div>
<button @click="changeData">
通过actions改变数据
</button>
</template>
<script>
import appStore from "@/store/app"
import {storeToRefs} from "pinia"
import {ref} from "vue"
export default {
name: "PiniaSubscribe",
setup() {
const store = appStore()
const {baseUrl} = storeToRefs(store)
const afterChangeUrl = 'https://www.taobao.com/'
let isShow = ref(false)
const subscribe = store.$subscribe((mutation, state) => {
/*
* mutation主要包含三个属性值:
* events:当前state改变的具体数据,包括改变前的值和改变后的值等等数据
* storeId:是当前store的id
* type:用于记录这次数据变化是通过什么途径,主要有三个分别是
* “direct” :通过 action 变化的
* ”patch object“ :通过 $patch 传递对象的方式改变的
* “patch function” :通过 $patch 传递函数的方式改变的
*
* */
// 我们就可以在此处监听store中值的变化,当变化为某个值的时候,去做一些业务操作之类的
console.log(mutation)
console.log(state.baseUrl)
if (state.baseUrl === afterChangeUrl) isShow.value = true
else isShow.value = false
}, {detached: false}) //第二个参数options对象,是各种配置参数
//detached:布尔值,默认是 false,正常情况下,当订阅所在的组件被卸载时,订阅将被停止删除,
// 如果设置detached值为 true 时,即使所在组件被卸载,订阅依然在生效
//参数还有immediate,deep,flush等等参数 和vue3 watch的参数是一样的,多的就不介绍了,用到再看文档吧
// 停止订阅
// subscribe() //调用上方声明的变量值,示例(subscribe),即可以停止订阅
function changeData() {
store.changeState(afterChangeUrl)
}
return {
isShow,
baseUrl,
changeData
}
}
}
</script>
https://blog.csdn.net/qq_42543244/article/details/123461832?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%9B%91%E5%90%ACpinia%E4%B8%ADstate%E5%8F%98%E5%8C%96&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-123461832.142v80insert_down38,201v4add_ask,239v2insert_chatgpt&spm=1018.2226.3001.4187
store.$onAction
对actions订阅监听
因为在订阅的时候,有错误异常的执行函数,所以,我们这里直接写上一个promise,当isError为false的时候,直接抛出异常
import {defineStore} from "pinia"
const userMainStore = defineStore('mainStore', {
state: () => ({
name: '小明'
}),
actions: {
changeName(name, isError) {
return new Promise((resolve, reject) => {
this.name = name
if (isError) {
resolve(`姓名:${this.name}`)
} else {
reject('error')
}
})
}
,
},
})
export default userMainStore
<template>
<div>
{{ name }}
</div>
<button @click="changeNameHong">改变名字为小红</button>
<button @click="changeNameMi">改变名字为小米</button>
</template>
<script>
import userMainStore from "@/store/main"
import {computed} from "vue";
export default {
name: "ActionSubcribe",
setup() {
const mainStore = userMainStore();
async function changeNameHong() {
let result = await mainStore.changeName('小红', true)
// console.log(result) //姓名:小红
}
async function changeNameMi() {
try {
let result = await mainStore.changeName('小米', false)
// console.log(result)
} catch (e) {
// console.log(e) // error
}
}
const unsubscribe = mainStore.$onAction((
{
name, //action 函数的名称
store, //store 实例,这里是 mainStore
args, //action 函数参数数组
after, //钩子函数,在action函数执行完成返回或者resolves后执行
onError // 钩子函数,在action函数报错或者rejects后执行
}) => {
console.log('name===>', name)
console.log('args===>', args)
console.log('store===>', store)
after(result => {
console.log('after result===>', result)
})
onError(error => {
console.log('onError error===>', error)
})
},
false //默认是false,设置为true的时候,组件卸载时,订阅依然有效
)
// 同样可以通过调用store.$onAction返回值,即unsubscribe 进行停止订阅
// unsubscribe() // 手动停止订阅
return {
name: computed(() => mainStore.name),
changeNameHong,
changeNameMi
}
}
}
</script>
八、使用函数的方式定义store
使用函数定义store时,所有的composition Api都可以使用