Pinia基本使用
Pinia的定义是使用pinia暴露出来的defineStore定义的
Setup Store
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
Option Store
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
console.log('this.doubleCount', this.doubleCount);
}
}
})
使用
直接引入store的定义, 调用store导出的变量即可使用
解构需要使用pinia提供的storeToRefs来保证数据的响应性
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter.js'
// 不使用解构方式
const counterStore = useCounterStore()
// 使用解构方式, 需要配合storeToRefs方法
const { count, doubleCount } = storeToRefs(counterStore)
const handleStoreIncrement = () => {
counterStore.increment()
}
</script>
<template>xxxx</template>
State
state推荐使用返回对象的方式来创建
import { defineStore } from "pinia";
export const useStateStore = defineStore('myState', {
state: () => {
return {
// 所有这些属性都将自动推断出它们的类型
count: 0,
name: 'Eduardo',
isAdmin: true,
items: [],
hasChanged: true,
}
}
});
修改state
使用$patch, 可以传入对象, 更推荐传入回调函数, 在回调函数内修改state;
传入对象方式造成修改state很难实现或者耗时,比如修改数组时需要创建一个新的集合, 而回调函数的方式则避免了以上问题.
<script setup>
import { useStateStore } from '@/stores/state.js'
const stateStore = useStateStore()
// 传入对象方式
const handle$patch1 = () => {
stateStore.$patch({
name: 'hhhhh'
})
}
// 传入回调函数方式
const handle$patch2 = () => {
stateStore.$patch(state => {
state.name = 'heiheihei';
})
}
</script>
重置state
$reset方法可以重置state
const store = useStore()
store.$reset()
订阅state
使用subscribe() 方法侦听 state 及其变化,(比起普通的 watch(),使用 $subscribe() 的好处是 subscriptions 在 patch 后只触发一次)
默认情况下,state subscription 会被绑定到添加它们的组件上(如果 store 在组件的 setup() 里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将 { detached: true } 作为第二个参数,以将 state subscription 从当前组件中分离
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
Getter
Getter完全等同于store中的state的计算值. 推荐使用箭头函数定义Getter
import { defineStore } from 'pinia'
import {useStateStore} from './state'
export const useGetterStore = defineStore('mygetter', {
state: () => {
return {
count: 1
}
},
getters: {
// 访问state
doubleCount(state) {
return state.count * 2
},
// 访问其他getter
tripleCount(state) {
return state.count * this.doubleCount / this.doubleCount * 3
},
// 访问其他store的getter
stateStoreCount(state) {
const stateStore = useStateStore()
return stateStore.count + state.count
}
}
});
访问state
如上例子, 直接访问state即可
访问其他getter
getter内的this能够访问本store下的其他getter, 例子如上
访问其他store的getter
直接引用其他store,直接使用即可, 例子如上
Action
action是在defineStore内actions对象内定义, action内可以进行异步操作
action定义
import { defineStore } from 'pinia'
// 模拟请求
const fetchMethod = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const randomNum = Math.random()
const isInteger = Number.parseInt((randomNum * 100)) % 2 === 0
if (isInteger) {
resolve(randomNum)
} else {
reject(randomNum)
}
}, 1000)
});
}
export const useActionStore = defineStore('myaction', {
state: () => {
return {
count: 2
}
},
actions: {
async increment() {
this.count = await fetchMethod()
return this.count;
}
}
})
订阅action
可以通过 store.onAction的回调函数是会在action本身之前执行, after会在promise解决后执行, onError会在抛出错误或者reject后执行
值得注意的是, 如上例子, await fetchMethod()后return了this.count, 这样才能在成功了后after内拿到result, 失败了后在onError内拿到error
<script setup>
import { useActionStore } from '@/stores/action.js'
const actionStore = useActionStore()
// 订阅action的变化
const unsubscribe = actionStore.$onAction(({name, store, args, after, onError}) => {
console.log(name);
console.log(store);
console.log(args);
after((result) => {
console.log('result', result);
});
onError((error) => {
console.log('error', error);
});
})
// 手动删除监听器
// unsubscribe()
</script>
Plugin
Plugin定义
Plugin是一个function, 调用pinia.use(pluginFunc)即可
export function myPiniaPlugin(context) {
context.pinia // 用 `createPinia()` 创建的 pinia。
context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
context.store // 该插件想扩展的 store
context.options // 定义传给 `defineStore()` 的 store 的可选对象。
// ...
}
例子:
import { ref, toRef } from "vue"
import { debounce } from 'lodash'
const sharedRef = ref('shared')
export const myPiniaPlugin = (context) => {
// 扩展store, 为store新增共享的属性
context.store.hello = ref('world')
context.store.shared = sharedRef
/**
* 添加新的state
* 1. $state上要添加, 才能被devtool识别, 才能被SSR正确序列化
* 2. store上要添加, 才能store.myState上访问到
*/
if (!Object.prototype.hasOwnProperty(context.store.$state, 'hasError')) {
context.store.$state.hasError = ref(false)
}
context.store.hasError = toRef(context.store.$state, 'hasError')
/**
* 插件中可以正常使用store.$subscribe和store.$onAction
*/
context.store.$subscribe(() => {
// 响应 store 变化
})
context.store.$onAction(() => {
// 响应 store actions
})
/**
* 添加新的选项
* 例如添加防抖
*/
if (context.options.debounce) {
return Object.keys(context.options.debounce).reduce((debounceAction, action) => {
console.log(debounceAction, action);
debounceAction[action] = debounce(
context.store[action],
context.options.debounce[action]
)
return debounceAction
}, {})
}
}
扩展Store
如上例子
添加新的state
- 在 store 上,然后你才可以用 store.myState 访问它。
- 在 store.$state 上,然后你才可以在 devtools 中使用它,并且,在 SSR 时被正确序列化(serialized)。
如上例子
插件中调用 $subscribe
如上例子
添加新的选项
如上例子, 同时store内需要提供需要的内容(如debounce这个option的定义)
import { defineStore } from 'pinia'
export const usePluginsBaseStore = defineStore('mypluginsbase', {
state: () => {
return {
count: 10
}
},
actions: {
searchContacts() {
console.log('searchContacts');
},
},
// 这将在后面被一个插件读取
debounce: {
searchContacts: 300
}
})