- 本文来源于 Home | Pinia 中文文档 (web3doc.top)
1、概述
- Pinia 是 Vue 的存储库,它容许您跨组件/页面共享状态。
1)比照 Vuex
- 更小,只有1kb;
- 更简略,actions 能够同时解决同步工作和异步工作;
- 不再有命名空间模块,然而各个 store 之间互相独立并且能够相互援用。
- 组合式 API 格调和良好的 TS 反对。
2)装置
Vue3 创立一个 pinia(根存储)并将其传递给应用程序
import { createPinia } from 'pinia'app.use(createPinia())
- Vue 2,您还须要装置一个插件并将创立的
pinia
注入应用程序的根目录 - 须要装置组合 API:
@vue/composition-api
import { createPinia, PiniaVuePlugin } from 'pinia'Vue.use(PiniaVuePlugin)const pinia = createPinia()new Vue({ el: '#app', // ... pinia,})
2、store
1)定义
Store 是应用
defineStore()
定义的,并且它须要一个惟一名称,作为第一个参数传递- 这个 name,也称为 id,是必要的,Pinia 应用它来将 store 连贯到 devtools。
import { defineStore } from 'pinia'// useStore 能够是 useUser、useCart 之类的任何货色// 第一个参数是应用程序中 store 的惟一 idexport const useStore = defineStore('main', { // other options...})
2)模块化
- 能够依据须要定义任意数量的 store ,并且您应该在不同的文件中定义每个 store 以充分利用 pinia。
3、state
在 Pinia 中,状态被定义为返回初始状态的函数。
import { defineStore } from 'pinia'const useStore = defineStore('storeId', { // 举荐应用 残缺类型推断的箭头函数 state: () => { return { // 所有这些属性都将主动推断其类型 counter: 0, name: 'Eduardo', isAdmin: true, } },})
1)拜访 “state”
- 默认状况下,您能够通过
store
实例拜访状态来间接读取和写入状态
const store = useStore()store.counter++
2)扭转状态
除了间接用
store.counter++
批改 store,你还能够调用$patch
办法。 它容许您应用局部“state”对象同时利用多个更改。store.$patch({ counter: store.counter + 1, name: 'Abalam',})
$patch
办法也承受一个函数来批量批改汇合内局部对象的状况:cartStore.$patch((state) => { state.items.push({ name: 'shoes', quantity: 1 }) state.hasChanged = true})
3)重置状态
- 能够通过调用 store 上的
$reset()
办法将状态 重置 到其初始值
const store = useStore()store.$reset()
4)替换state
能够通过将其
$state
属性设置为新对象来替换 Store 的整个状态:store.$state = { counter: 666, name: 'Paimon' }
5)订阅状态
- 能够通过 store 的
$subscribe()
办法查看状态及其变动。 - 与惯例的
watch()
相比,应用$subscribe()
的长处是 subscriptions 只会在 patches 之后触发一次。 当组件被卸载时,它们将被主动删除。 如果要在卸载组件后保留它们,请将
{ detached: true }
作为第二个参数传递给 detach 以后组件的 state subscription:export default { setup() { const someStore = useSomeStore() // 此订阅将在组件卸载后保留 someStore.$subscribe(callback, { detached: true }) // ... },}
4、getters
Getter 齐全等同于 Store 状态 的 computed
- 箭头函数接管 state 作为第一个参数。
- 惯例函数通过 this 拜访到整个 store 实例,然而须要定义返回类型。
export const useStore = defineStore('main', { state: () => ({ counter: 0, }), getters: { // 主动将返回类型推断为数字 doubleCount(state) { return state.counter * 2 }, // 返回类型必须明确设置 doublePlusOne(): number { return this.counter * 2 + 1 }, },})
1)拜访其余 getter
- 与计算属性一样,您能够组合多个 getter。 通过
this
拜访任何其余 getter。
export const useStore = defineStore('main', { state: () => ({ counter: 0, }), getters: { // 类型是主动推断的,因为咱们没有应用 `this` doubleCount: (state) => state.counter * 2, doubleCountPlusOne() { return this.doubleCount + 1 }, },})
2)将参数传递给 getter
Getters 只是幕后的 computed 属性,因而无奈向它们传递任何参数。 然而,您能够从 getter 返回一个函数以承受任何参数:
export const useStore = defineStore('main', { getters: { getUserById: (state) => { return (userId) => state.users.find((user) => user.id === userId) }, },})// App.vue<template> <p>User 2: {{ getUserById(2) }}</p></template>
在执行此操作时,getter 不再缓存,它们只是您调用的函数。 然而,您能够在 getter 自身外部利用闭包缓存一些后果。
export const useStore = defineStore('main', { getters: { getActiveUserById(state) { const activeUsers = state.users.filter((user) => user.active) return (userId) => activeUsers.find((user) => user.id === userId) }, },})
3)拜访其余 Store 的getter
- 要应用其余存储 getter,您能够间接在 getter 外部应用它
import { useOtherStore } from './other-store'export const useStore = defineStore('main', { state: () => ({ // ... }), getters: { otherGetter(state) { const otherStore = useOtherStore() return state.localData + otherStore.data }, },})
5、actions
- Actions 相当于组件中的 methods,能够通过 this 拜访到整个 store 实例。
export const useStore = defineStore('main', { state: () => ({ counter: 0, }), actions: { increment() { this.counter++ }, randomizeCounter() { this.counter = Math.round(100 * Math.random()) }, },})
actions
能够是异步的,您能够在其中await
任何 API 调用甚至其余操作!
export const useUsers = defineStore('users', { state: () => ({ userData: null, // ... }), actions: { async registerUser(login, password) { try { this.userData = await api.post({ login, password }) showTooltip(`Welcome back ${this.userData.name}!`) } catch (error) { showTooltip(error) // 让表单组件显示谬误 return error } }, },})
1)拜访其余 store 操作
- 要应用另一个 store ,您能够间接在 action 外部应用它:
import { useAuthStore } from './auth-store'export const useSettingsStore = defineStore('settings', { state: () => ({ // ... }), actions: { async fetchUserPreferences(preferences) { const auth = useAuthStore() if (auth.isAuthenticated) { this.preferences = await fetchPreferences() } else { throw new Error('User must be authenticated') } }, },})
2)订阅 Actions
- 能够应用
store.$onAction()
订阅 action 及其后果。 传递给它的回调在 action 之前执行。 after
解决 Promise 并容许您在 action 实现后执行函数。onError
容许您在解决中抛出谬误。 这些对于在运行时跟踪谬误很有用 。
const unsubscribe = someStore.$onAction( ({ name, // action 的名字 store, // store 实例 args, // 调用这个 action 的参数 after, // 在这个 action 执行结束之后,执行这个函数 onError, // 在这个 action 抛出异样的时候,执行这个函数 }) => { // 记录开始的工夫变量 const startTime = Date.now() // 这将在 `store` 上的操作执行之前触发 console.log(`Start "${name}" with params [${args.join(', ')}].`) // 如果 action 胜利并且齐全运行后,after 将触发。 // 它将期待任何返回的 promise after((result) => { console.log( `Finished "${name}" after ${ Date.now() - startTime }ms.\nResult: ${result}.` ) }) // 如果 action 抛出或返回 Promise.reject ,onError 将触发 onError((error) => { console.warn( `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.` ) }) })// 手动移除订阅unsubscribe()
当组件被卸载时,它们将被主动删除。 如果要在卸载组件后保留它们,请将
true
作为第二个参数传递给以后组件的 detach action subscription:export default { setup() { const someStore = useSomeStore() // 此订阅将在组件卸载后保留 someStore.$onAction(callback, true) // ... },}
6、plugins
- 应用
pinia.use()
将插件增加到 pinia 实例中。 - 装置此插件后创立的每个store增加一个名为
secret
的属性
import { createPinia } from 'pinia'// 这可能在不同的文件中function SecretPiniaPlugin() { return { secret: 'the cake is a lie' }}const pinia = createPinia()// 将插件提供给 piniapinia.use(SecretPiniaPlugin)// 在另一个文件中const store = useStore()store.secret // 'the cake is a lie'
- Pinia 插件是一个函数,能够抉择返回要增加到 store 的属性。 它须要一个可选参数,一个 context:
export function myPiniaPlugin(context) { context.pinia // 应用 `createPinia()` 创立的 pinia context.app // 应用 `createApp()` 创立的以后应用程序(仅限 Vue 3) context.store // 插件正在裁减的 store context.options // 定义存储的选项对象传递给`defineStore()` // ...}
1)增加新的内部属性
当增加内部属性、来自其余库的类实例或仅仅是非响应式的货色时,您应该在将对象传递给 pinia 之前应用
markRaw()
包装对象。 这是一个将路由增加到每个 store 的示例:import { markRaw } from 'vue'// 依据您的路由所在的地位进行调整import { router } from './router'pinia.use(({ store }) => { store.router = markRaw(router)})
2)在插件中调用 $subscribe
您也能够在插件中应用 store.subscribe和store.onAction
pinia.use(({ store }) => { store.$subscribe(() => { // 在存储变动的时候执行 }) store.$onAction(() => { // 在 action 的时候执行 })})
3)引入新的 store 属性
向 store 增加新属性时,您还应该扩大
PiniaCustomProperties
接口。import 'pinia'declare module 'pinia' { export interface PiniaCustomProperties { // 通过应用 setter,咱们能够同时容许字符串和援用 set hello(value: string | Ref<string>) get hello(): string // 你也能够定义更简略的值 simpleNumber: number }}
而后能够平安地写入和读取它:
pinia.use(({ store }) => { store.hello = 'Hola' store.hello = ref('Hola') store.number = Math.random() // @ts-expect-error: we haven't typed this correctly store.number = ref(Math.random())})