多亏了一个低级API
, Pinia stores
可以完全扩展。下面是一些你可以做的事情清单:
stores
增加新属性stores
时添加新选项stores
增加新方法actions
Local Storage
一样的副作用stores
使用pinia.use()
将插件添加到pinia
实例中。最简单的例子是通过返回一个对象向所有stores
添加一个静态属性:
import { createPinia } from 'pinia'
// 为每个store增加一个名为`secret`的属性,在插件被安装创建之后
// 可以在不同的文件中
function SecretPiniaPlugin() {
return { secret: 'the cake is a lie' }
}
const pinia = createPinia()
// 给pinia传入插件
pinia.use(SecretPiniaPlugin)
// 在其他文件中
const stroe = useStore()
store.secret // the cake is a lie
一个Pinia
插件是一个函数,可以选择返回要添加到store
中的属性,它接收一个可选参数,context
:
export function myPiniaPlugin(context) {
context.pinia // 通过`createPinia()`创建的pinia
context.app // 通过`createApp()`创建的当前实例 (Vue 3 only)
context.store // 插件的store库
context.options // 在定义 store时传给 `defineStore()`的参数
// ...
}
这个函数被通过pinia.use()
传递给pinia
:
pinia.use(myPiniaPlugin)
Plugins
只应用于stroes
创建后,pinia
被传递到应用程序,否则他们不会被应用。
你可以通过在插件中简单地返回一个属性对象来为每个store
添加属性:
pinia.use(() => ({ hello: 'world' }))
你也可以直接在store
中设置属性,但是如果可能的话,使用return
的返回版本,以便他们在devtools
中自动被跟踪:
pinia.use(({ store } => {
store.hello = 'world'
}))
插件返回的任何属性都会被devtools
自动跟踪,所以为了让hello
在devtools
中可见,如果你想在devtools
中调试,确保在开发模式下增加store._customProperties
:
pinia.use(({ store }) => {
store.hello = 'world'
// 请确保您的绑定程序处理此问题。Webpack和vite在默认情况下应该会这样做
if(process.env.NODE_ENV === 'development') {
// 添加任何你想在store中调试的key
store._customProperties.add('hello')
}
})
需要注意的是,每个store
都会使用响应式包装,并且会自动打开它包含的任何Ref ``(Ref (), computed(),…)
:
const sharedRef = ref('shared')
pinia.use(({ store }) => {
// 每个store都有一个单独的hello
store.hello = ref('secret')
// 他会自动解包
store.hello // 'secret'
// 所有的store将会共享`shared`的值
store.shared = sharedRef
store.shared // 'shared'
})
这就是为什么你可以接收所有的计算属性而不用.value
和为什么他们是响应式的。
可以在定义store
时创建新的选项,以便以后从插件中使用它们。例如,你可以创建一个debounce
选项,允许你对任何操作进行debounce
:
defineStore('search', {
actions: {
searchContacts() {
// ...
},
},
// 在后续的插件中可以被读取
debounce: {
// 防抖函数可以将searchContacts延迟300ms
searchContacts: 300
}
})
插件可以读取这个选项,重新包装替代原始的值:
// 使用第三方库
import debounce from 'lodash/debunce'
pinia.use(({ options, store }) => {
if (options.debounce) {
// 可以重写actions
return Object.keys(options.debounce).reduce((debouncedActions, action) => {
debouncedActions[action] = debounce(
store[action],
options.debounce[action]
)
return debouncedActions
}, {})
}
})
请注意,当使用setup
语法时,自定义参数作为第三个参数被传入:
defineStore(
'search',
() => {
// ...
},
{
// 在后续的插件中可以被读取
debounce: {
// 防抖函数可以将searchContacts延迟300ms
searchContacts: 300
}
}
)
上面展示的一切都可以被类型支持,所以你不需要使用any
或者@ts-ignore
Pinia
插件的类型如下:
import { PiniaPluginContext } from 'pinia'
export function myPiniaPlugin(context: PiniaPluginContext) {
// ...
}
当你为stores
增加一个新属性时,你也应该为PiniaCustomProperties
拓展一个接口类型。
import 'pinia'
declare module 'pinia' {
export interface PiniaCustomProperties {
// 通过使用setter,我们可以同时允许string和refs
set hello(value: string | Ref<string>)
get hello(): string
// 也可以定义简单值
simpleNumber: number
}
}
它可以被重写和安全读取
pinia.use(({ store }) => {
store.hello = 'Hola'
store.hello = ref('Hola')
store.simpleNumber = Math.random()
// 错误的类型,报错
store.simpleNumber = ref(Math.random())
})
PiniaCustomProperties
是一个泛型类型,允许您引用store
的属性。想象一下下面的例子,我们将初始选项复制为$options
(这只适用于选项stores
):
pinia.use(({ options }) => ({ $options: options }))
我们可以通过使用PiniaCustomProperties
的4个泛型类型来正确地输入它:
import 'pinia'
declare module 'pinia' {
export interface PiniaCustomProperties<Id, S, G, A> {
$options: {
id: Id
state?: () => S
getters?: G
actions?: A
}
}
}
Tip
当在泛型中扩展类型时,它们的命名必须与源代码中的名称完全一致。
Id
不能命名为id
或I
,S
不能命名为State
。以下是每个字母所代表的含义:
- S: State
- G: Getters
- A: Actions
- SS: Setup Store / Store
当添加新的状态属性时(同时添加到store
和store.$state
),您需要将类型添加到PiniaCustomStateProperties
。与PiniaCustomProperties
不同的是,它只接收State
泛型:
import 'pinia'
declare module 'pinia' {
export interface PiniaCustomStateProperties<S> {
hello: string
}
}
当为defineStore()
创建新选项时,您应该扩展DefineStoreOptionsBase
。与PiniaCustomProperties
不同的是,它只公开两种泛型:State
和Store
类型,允许您限制可以定义的类型。例如,你可以使用action
的名称:
import 'pinia'
declare module 'pinia' {
export interface DefineStoreOptionsBase<S, Store> {
// allow defining a number of ms for any of the actions
debounce?: Partial<Record<keyof StoreActions<Store>, number>>
}
}
Tip
还有一个StoreGetters
类型用于从Store
类型中提取getter
。您还可以通过分别扩展DefineStoreOptions
和DefineSetupStoreOptions
类型来扩展setup stores
或option stores
的选项。