前言
【pinia 源码】系列文章次要剖析 pinia
的实现原理。该系列文章源码参考pinia v2.0.14
。
源码地址:https://github.com/vuejs/pinia
官网文档:https://pinia.vuejs.org
本篇文章将剖析 defineStore
的实现。
应用
通过 defineStore
定义一个store
。
const useUserStore = defineStore('counter', {state: () => ({count: 0}),
actions: {increment() {this.count++}
}
})
// or
const useUserStore = defineStore({
id: 'counter',
state: () => ({count: 0}),
actions: {increment() {this.count++}
}
})
// or
const useUserStore = defineStore('counter', () => {const count = ref(0)
function increment() {count.value++}
return {count, increment}
})
defineStore
export function defineStore(
idOrOptions: any,
setup?: any,
setupOptions?: any
): StoreDefinition {
let id: string
let options:
| DefineStoreOptions<
string,
StateTree,
_GettersTree<StateTree>,
_ActionsTree
>
| DefineSetupStoreOptions<
string,
StateTree,
_GettersTree<StateTree>,
_ActionsTree
>
const isSetupStore = typeof setup === 'function'
if (typeof idOrOptions === 'string') {
id = idOrOptions
options = isSetupStore ? setupOptions : setup
} else {
options = idOrOptions
id = idOrOptions.id
}
function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric {// ...}
useStore.$id = id
return useStore
}
defineStore
函数能够接管三个参数:idOrOptions
、setup
、setOptions
,后两个参数为可选参数。上面是三个 defineStore
的函数类型定义。
export function defineStore<
Id extends string,
S extends StateTree = {},
G extends _GettersTree<S> = {},
A /* extends ActionsTree */ = {}
>(
id: Id,
options: Omit<DefineStoreOptions<Id, S, G, A>, 'id'>
): StoreDefinition<Id, S, G, A>
export function defineStore<
Id extends string,
S extends StateTree = {},
G extends _GettersTree<S> = {},
A /* extends ActionsTree */ = {}
>(options: DefineStoreOptions<Id, S, G, A>): StoreDefinition<Id, S, G, A>
export function defineStore<Id extends string, SS>(
id: Id,
storeSetup: () => SS,
options?: DefineSetupStoreOptions<
Id,
_ExtractStateFromSetupStore<SS>,
_ExtractGettersFromSetupStore<SS>,
_ExtractActionsFromSetupStore<SS>
>
): StoreDefinition<
Id,
_ExtractStateFromSetupStore<SS>,
_ExtractGettersFromSetupStore<SS>,
_ExtractActionsFromSetupStore<SS>
>
首先在 defineStore
中申明了三个变量:id
、options
、isSetupStore
,其中 id
为定义的 store
的惟一 id
,options
为定义 store
时的 options
,isSetupStore
代表传入的 setup
是不是个函数。
而后依据传入的 idOrOptions
的类型,为 id
、otions
赋值。紧接着申明了一个 useStore
函数,并将 id
赋给它,而后将其 return
。截止到此,咱们晓得defineStore
会返回一个函数,那么这个函数具体是做什么的呢?咱们持续看 useStore
的实现。
useStore
function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric {
// 获取以后实例
const currentInstance = getCurrentInstance()
// 测试环境下,疏忽提供的参数,因为总是能应用 getActivePinia()获取 pinia 实例
// 非测试环境下,如果未传入 pinia,则会从组件中应用 inject 获取 pinia
pinia =
(__TEST__ && activePinia && activePinia._testing ? null : pinia) ||
(currentInstance && inject(piniaSymbol))
// 设置激活的 pinia
if (pinia) setActivePinia(pinia)
// 如果没有 activePinia,那么可能没有 install pinia,开发环境下进行提醒
if (__DEV__ && !activePinia) {
throw new Error(`[🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?\n` +
`\tconst pinia = createPinia()\n` +
`\tapp.use(pinia)\n` +
`This will fail in production.`
)
}
// 设置 pinia 为激活的 pinia
pinia = activePinia!
// 从 pina._s 中查找 id 否注册过,如果没有被注册,创立一个 store 并注册在 pinia._s 中
if (!pinia._s.has(id)) {if (isSetupStore) {createSetupStore(id, setup, options, pinia)
} else {createOptionsStore(id, options as any, pinia)
}
if (__DEV__) {useStore._pinia = pinia}
}
// 从 pinia._s 中获取 id 对应的 store
const store: StoreGeneric = pinia._s.get(id)!
if (__DEV__ && hot) {
const hotId = '__hot:' + id
const newStore = isSetupStore
? createSetupStore(hotId, setup, options, pinia, true)
: createOptionsStore(hotId, assign({}, options) as any, pinia, true)
hot._hotUpdate(newStore)
// cleanup the state properties and the store from the cache
delete pinia.state.value[hotId]
pinia._s.delete(hotId)
}
if (
__DEV__ &&
IS_CLIENT &&
currentInstance &&
currentInstance.proxy &&
!hot
) {
const vm = currentInstance.proxy
const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})
cache[id] = store
}
// 返回 store
return store as any
}
useStore
接管两个可选参数:pinia
、hot
。pinia
是个 Pinia
的实例,而 hot
只在开发环境下有用,它与模块的热更新无关。
在 useStore
中会首先获取以后组件实例,如果存在组件实例,应用 inject(piniaSymbol)
获取 pinia
(在install
中会进行 provide
),并将其设置为activePinia
,而后在activePinia._s
中查找是否有被注册为 id
的store
,如果没有则创立 store
,将其注册到activePinia._s
中。最初返回 activePinia._s
中id
对应的store
。
当初咱们晓得 useStore
函数,最终会返回一个 store
。那么这个store
是什么呢?它是如何创立的呢?在 useStore
中依据不同状况中有两中形式来创立 store
,别离是:createSetupStore
、createOptionsStore
。这两个形式的应用条件是:如果defineStore
第二个参数是个 function
调用createSetupStore
,相同调用createOptionsStore
。
createSetupStore
createSetupStore
函数代码过长,这里就不贴残缺代码了。createSetupStore
可接管参数如下:
参数 | 阐明 | |
---|---|---|
$id |
定义 store 的id |
|
setup |
一个能够返回 state 的函数 |
|
options |
defineStore 的options |
|
pinia |
Pinia 实例 |
|
hot |
是否启用热更新 | 可选 |
isOptionsStore |
是否应用 options 申明的store |
可选 |
createSetupStore
代码有 500 多行,如果从头开始看的话,不容易了解。咱们能够依据 createSetupStore
的用处,从其外围开始看。因为 createSetupStore
是须要创立 store
,并将store
注册到 pinia._s
中,所以 createSetupStore
中可能须要创立 store
,咱们找到创立store
的中央。
const partialStore = {
_p: pinia,
// _s: scope,
$id,
$onAction: addSubscription.bind(null, actionSubscriptions),
$patch,
$reset,
$subscribe(callback, options = {}) {
const removeSubscription = addSubscription(
subscriptions,
callback,
options.detached,
() => stopWatcher()
)
const stopWatcher = scope.run(() =>
watch(() => pinia.state.value[$id] as UnwrapRef<S>,
(state) => {if (options.flush === 'sync' ? isSyncListening : isListening) {
callback(
{
storeId: $id,
type: MutationType.direct,
events: debuggerEvents as DebuggerEvent,
},
state
)
}
},
assign({}, $subscribeOptions, options)
)
)!
return removeSubscription
},
$dispose,
} as _StoreWithState<Id, S, G, A>
if (isVue2) {partialStore._r = false}
const store: Store<Id, S, G, A> = reactive(
assign(
__DEV__ && IS_CLIENT
? // devtools custom properties
{_customProperties: markRaw(new Set<string>()),
_hmrPayload,
}
: {},
partialStore
)
) as unknown as Store<Id, S, G, A>
pinia._s.set($id, store)
store
是用 reactive
包装的一个响应式对象,reactive
所包装的对象是由 partialStore
通过 Object.assign
进行复制的。partialStore
中定义了很多办法,这些办法都是裸露给用户操作 store
的一些接口,如 $onAction
可设置 actions
的回调、$patch
可更新 store
中的 state
、$dispose
可销毁store
。
在调用完 pinia._s.set($id, store)
之后,会执行 setup
,获取所有的数据。setup
的执行会在创立 pinia
实例时创立的 effectScope
中运行,而且会再独自创立一个effectScope
,用来独自执行setup
.
const setupStore = pinia._e.run(() => {scope = effectScope()
return scope.run(() => setup())
})!
而后遍历 setupStore
的属性:如果 prop
(key
对应的值)为 ref
(不为computed
)或reactive
,则将key
及prop
同步到 pina.state.value[$id]
中;如果 prop
为function
,则会应用 wrapAction
包装 prop
,并将包装后的办法赋值给setupStore[key]
,以笼罩之前的值,同时将包装后的办法存入optionsForPlugin.actions
中。
for (const key in setupStore) {const prop = setupStore[key]
// 如果 prop 是 ref(但不是 computed)或 reactive
if ((isRef(prop) && !isComputed(prop)) || isReactive(prop)) {if (__DEV__ && hot) {set(hotState.value, key, toRef(setupStore as any, key))
} else if (!isOptionsStore) {if (initialState && shouldHydrate(prop)) {if (isRef(prop)) {prop.value = initialState[key]
} else {mergeReactiveObjects(prop, initialState[key])
}
}
// 将对应属性同步至 pinia.state 中
if (isVue2) {set(pinia.state.value[$id], key, prop)
} else {pinia.state.value[$id][key] = prop
}
}
if (__DEV__) {_hmrPayload.state.push(key)
}
} else if (typeof prop === 'function') { // 如果 prop 是 function
// 应用 wrapAction 包装 prop,在 wrapAction 会解决 afeterCallback、errorCallback
const actionValue = __DEV__ && hot ? prop : wrapAction(key, prop)
// 将 actionsValue 增加到 setupStore 中,笼罩原来的 function
if (isVue2) {set(setupStore, key, actionValue)
} else {setupStore[key] = actionValue
}
if (__DEV__) {_hmrPayload.actions[key] = prop
}
// 将 function 类型的 prop 存入 optionsForPlugin.actions 中
optionsForPlugin.actions[key] = prop
} else if (__DEV__) {if (isComputed(prop)) {_hmrPayload.getters[key] = isOptionsStore
? // @ts-expect-error
options.getters[key]
: prop
if (IS_CLIENT) {const getters: string[] =
setupStore._getters || (setupStore._getters = markRaw([]))
getters.push(key)
}
}
}
}
接下来咱们看下 wrapAction
是如何进行包装 function
类型上的prop
。
function wrapAction(name: string, action: _Method) {return function (this: any) {setActivePinia(pinia)
const args = Array.from(arguments)
const afterCallbackList: Array<(resolvedReturn: any) => any> = []
const onErrorCallbackList: Array<(error: unknown) => unknown> = []
function after(callback: _ArrayType<typeof afterCallbackList>) {afterCallbackList.push(callback)
}
function onError(callback: _ArrayType<typeof onErrorCallbackList>) {onErrorCallbackList.push(callback)
}
triggerSubscriptions(actionSubscriptions, {
args,
name,
store,
after,
onError,
})
let ret: any
try {ret = action.apply(this && this.$id === $id ? this : store, args)
} catch (error) {triggerSubscriptions(onErrorCallbackList, error)
throw error
}
// 如果后果是 promise,在 promise 中触发 afterCallbackList 及 onErrorCallbackList
if (ret instanceof Promise) {
return ret
.then((value) => {triggerSubscriptions(afterCallbackList, value)
return value
})
.catch((error) => {triggerSubscriptions(onErrorCallbackList, error)
return Promise.reject(error)
})
}
triggerSubscriptions(afterCallbackList, ret)
return ret
}
}
wrapAction
首先返回一个函数,在这个函数中,首先将 pinia
设置为 activePinia
,触发actionSubscriptions
中的函数,而后执行 action
函数,如果执行过程中出错,会执行 onErrorCallbackList
中的 errorCallback
,如果没有出错的话,执行afterCallbackList
中的 afterCallback
,最初将action
的返回后果return
。
wrapAction
中的 actionSubscriptions
是个什么呢?
其实 actionSubscriptions
中的 callback
就是是通过 store.$onAction
增加的回调函数;在执行 actionSubscriptions
中的 callback
过程中,会将对应 callback
增加到 afterCallbackList
或onErrorCallbackList
中。例如:
store.$onAction(({after, onError, name, store}) => {after((value) => {console.log(value)
})
onError((error) => {console.log(error)
})
})
遍历完 setupStore
之后,会将 setupStore
合并至 store
和store
的原始对对象中,以方便使用 storeToRefs()
检索响应式对象。
if (isVue2) {Object.keys(setupStore).forEach((key) => {
set(
store,
key,
setupStore[key]
)
})
} else {assign(store, setupStore)
assign(toRaw(store), setupStore)
}
紧接着拦挡 store.$state
的get
、set
办法:当调用 store.$state
时,可能从 pinia.state.value
找到对应的 state
;当应用store.$state = xxx
去批改值时,则调用 $patch
办法批改值。
Object.defineProperty(store, '$state', {get: () => (__DEV__ && hot ? hotState.value : pinia.state.value[$id]),
set: (state) => {
/* istanbul ignore if */
if (__DEV__ && hot) {throw new Error('cannot set hotState')
}
$patch(($state) => {assign($state, state)
})
},
})
截止到此,store
就筹备结束。如果在 Vue2
环境下,会将 store._r
设置为 true。
if (isVue2) {store._r = true}
接下来就须要调用应用 use
办法注册的plugins
:
pinia._p.forEach((extender) => {if (__DEV__ && IS_CLIENT) {const extensions = scope.run(() =>
extender({
store,
app: pinia._a,
pinia,
options: optionsForPlugin,
})
)!
Object.keys(extensions || {}).forEach((key) =>
store._customProperties.add(key)
)
assign(store, extensions)
} else {
// 将 plugin 的后果合并到 store 中
assign(
store,
scope.run(() =>
extender({
store,
app: pinia._a,
pinia,
options: optionsForPlugin,
})
)!
)
}
})
最初返回store
。
if (
initialState &&
isOptionsStore &&
(options as DefineStoreOptions<Id, S, G, A>).hydrate
) {;(options as DefineStoreOptions<Id, S, G, A>).hydrate!(
store.$state,
initialState
)
}
isListening = true
isSyncListening = true
return store
接下来看下 store
中的几个办法:
$onAction
在每个 action
中增加回调函数。回调接管一个对象参数:该对象蕴含 name
(action
的key
值)、store
(以后 store
)、after
(增加action
执行完之后的回调)、onError
(增加 action
执行过程中的谬误回调)、args
(action
的参数)属性。
示例:
// 统计 add action 的调用次数
let count = 0, successCount = 0, failCount = 0
store.$onAction(({name, after, onError}) => {if (name === 'add') {
count++
after((resolveValue) => {
successCount++
console.log(resolveValue)
})
onError((error) => {
failCount++
console.log(error)
})
}
})
$onAction
外部通过公布订阅模式实现。在 pinia
中有个专门的订阅模块subscriptions.ts
,其中蕴含两个次要办法:addSubscription
(增加订阅)、triggerSubscriptions
(触发订阅)。
addSubscription
可接管四个参数:subscriptions
(订阅列表)、callback
(增加的订阅函数)、detached
(游离的订阅,如果为 false
在组件卸载后,主动移除订阅;如果为true
,不会主动移除订阅)、onCleanup
(订阅被移除时的回调)
triggerSubscriptions
接管两个参数:subscriptions
(订阅列表)、args
(action
的参数列表)
export function addSubscription<T extends _Method>(subscriptions: T[],
callback: T,
detached?: boolean,
onCleanup: () => void = noop) {subscriptions.push(callback)
const removeSubscription = () => {const idx = subscriptions.indexOf(callback)
if (idx > -1) {subscriptions.splice(idx, 1)
onCleanup()}
}
if (!detached && getCurrentInstance()) {onUnmounted(removeSubscription)
}
return removeSubscription
}
export function triggerSubscriptions<T extends _Method>(subscriptions: T[],
...args: Parameters<T>
) {subscriptions.slice().forEach((callback) => {callback(...args)
})
}
$onAction
通过 addSubscription.bind(null, actionSubscriptions)
实现。
如何触发订阅?
首先在 store
的初始化过程中,会将 action
应用 wrapAction
函数进行包装,wrapAction
返回一个函数,在这个函数中会先触发 actionSubscriptions
,这个触发过程中会将afterCallback
、onErrorCallback
增加到对应列表。而后调用 action
,如果调用过程中出错,则触发onErrorCallbackList
,否则触发afterCallbackList
。如果action
的后果是 Promise
的话,则在 then
中触发 onErrorCallbackList
,在catch
中触发 onErrorCallbackList
。而后会将包装后的action
笼罩原始 action
,这样每次调用action
时就是调用的包装后的action
。
$patch
应用 $patch
能够更新 state
的值,可进行批量更新。$patch
接管一个 partialStateOrMutator
参数,它能够是个对象也能够是个办法。
示例:
store.$patch((state) => {
state.name = 'xxx'
state.age = 14
})
// or
store.$patch({
name: 'xxx',
age: 14
})
$patch
源码:
function $patch(
partialStateOrMutator:
| _DeepPartial<UnwrapRef<S>>
| ((state: UnwrapRef<S>) => void)
): void {
// 合并的相干信息
let subscriptionMutation: SubscriptionCallbackMutation<S>
// 是否触发状态批改后的回调,isListening 代表异步触发,isSyncListening 代表同步触发
// 此处先敞开回调的触发,避免批改 state 的过程中频繁触发回调
isListening = isSyncListening = false
if (__DEV__) {debuggerEvents = []
}
// 如果 partialStateOrMutator 是个 function,执行办法,传入以后的 store
if (typeof partialStateOrMutator === 'function') {partialStateOrMutator(pinia.state.value[$id] as UnwrapRef<S>)
subscriptionMutation = {
type: MutationType.patchFunction,
storeId: $id,
events: debuggerEvents as DebuggerEvent[],}
} else { // 如果不是 function,则调用 mergeReactiveObjects 合并 state
mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator)
subscriptionMutation = {
type: MutationType.patchObject,
payload: partialStateOrMutator,
storeId: $id,
events: debuggerEvents as DebuggerEvent[],}
}
// 当合并完之后,将 isListening、isSyncListening 设置为 true,意味着能够触发状态扭转后的回调函数了
const myListenerId = (activeListener = Symbol())
nextTick().then(() => {if (activeListener === myListenerId) {isListening = true}
})
isSyncListening = true
// 因为在批改 pinia.state.value[$id]的过程中敞开(isSyncListening 与 isListening)了监听,所以须要手动触发订阅列表
triggerSubscriptions(
subscriptions,
subscriptionMutation,
pinia.state.value[$id] as UnwrapRef<S>
)
}
$reset
通过构建一个新的 state object
将state
重置为初始状态。只在 options
配置下失效。如果是 setup
配置,开发环境下报错。
store.$reset = function $reset() {
// 从新执行 state,获取一个新的 state
const newState = state ? state() : {}
// 通过 $patch,应用 assign 将 newState 合并到 $state 中
this.$patch(($state) => {assign($state, newState)
})
}
$subscribe
设置 state
扭转后的回调,返回一个移除回调的函数。可承受两个参数:callback
(增加的回调函数)、options:{detached, flush, ...watchOptions}
(detached
同 addSubscription
中的 detached
;flush
代表是否同步触发回调,可取值:sync
)。
示例:
store.$subribe((mutation: {storeId, type, events}, state) => {console.log(storeId)
console.log(type)
console.log(state)
}, {detached: true, flush: 'sync'})
$subscribe
源码:
function $subscribe(callback, options = {}) {
// 将 callback 增加到 subscriptions 中,以便应用 $patch 更新状态时,触发回调
// 当应用 removeSubscription 移除 callback 时,进行对 pinia.state.value[$id]监听
const removeSubscription = addSubscription(
subscriptions,
callback,
options.detached,
() => stopWatcher()
)
const stopWatcher = scope.run(() =>
// 监听 pinia.state.value[$id],以触发 callback,当应用 $patch 更新 state 时,不会进入触发这里的 callback
watch(() => pinia.state.value[$id] as UnwrapRef<S>,
(state) => {if (options.flush === 'sync' ? isSyncListening : isListening) {
callback(
{
storeId: $id,
type: MutationType.direct,
events: debuggerEvents as DebuggerEvent,
},
state
)
}
},
assign({}, $subscribeOptions, options)
)
)!
return removeSubscription
}
在 callback
中的第一个参数中有个 type
属性,示意是通过什么形式更新的state
,它有三个值:
MutationType.direct
:通过state.name='xxx'
/store.$state.name='xxx'
等形式批改MutationType.patchObject
:通过store.$patch({name: 'xxx'})
形式批改MutationType.patchFunction
:通过store.$patch((state) => state.name='xxx')
形式批改
$dispose
销毁store
。
function $dispose() {
// 进行监听
scope.stop()
// 清空 subscriptions 及 actionSubscriptions
subscriptions = []
actionSubscriptions = []
// 从 pinia._s 中删除 store
pinia._s.delete($id)
}
createOptionsStore
createOptionsStore
可接管参数如下:
参数 | 阐明 | |
---|---|---|
id |
定义 store 的id |
|
options |
defineStore 的options |
|
pinia |
Pinia 实例 |
|
hot |
是否启用热更新 | 可选 |
function createOptionsStore<
Id extends string,
S extends StateTree,
G extends _GettersTree<S>,
A extends _ActionsTree
>(
id: Id,
options: DefineStoreOptions<Id, S, G, A>,
pinia: Pinia,
hot?: boolean
): Store<Id, S, G, A> {const { state, actions, getters} = options
const initialState: StateTree | undefined = pinia.state.value[id]
let store: Store<Id, S, G, A>
function setup() {// 如果 pinia.state.value[id]不存在,进行初始化
if (!initialState && (!__DEV__ || !hot)) {if (isVue2) {set(pinia.state.value, id, state ? state() : {})
} else {pinia.state.value[id] = state ? state() : {}
}
}
// 将 pinia.state.value[id]各属性值转为响应式对象
const localState =
__DEV__ && hot
? // use ref() to unwrap refs inside state TODO: check if this is still necessary
toRefs(ref(state ? state() : {}).value)
: toRefs(pinia.state.value[id])
// 解决 getters,并将解决后的 getters 和 actions 合并到 localState 中
return assign(
localState,
actions,
Object.keys(getters || {}).reduce((computedGetters, name) => {computedGetters[name] = markRaw(computed(() => {setActivePinia(pinia)
const store = pinia._s.get(id)!
if (isVue2 && !store._r) return
return getters![name].call(store, store)
})
)
return computedGetters
}, {} as Record<string, ComputedRef>)
)
}
// 利用 createSetupStore 创立 store
store = createSetupStore(id, setup, options, pinia, hot, true)
// 重写 store.$reset
store.$reset = function $reset() {const newState = state ? state() : {}
this.$patch(($state) => {assign($state, newState)
})
}
return store as any
}
在 createOptionsStore
中会依据传入参数结构一个 setup
函数,而后通过 createSetupStore
创立一个 store
,并重写store.$reset
办法,最初返回store
。
这个 setup
函数中会将 state()
的返回值赋值给 pinia.state.value[id]
,而后将pinia.state.value[id]
进行 toRefs
,失去localState
,最初将解决后的getters
和actions
都合并到 localState
中,将其返回。对于 getters
的解决:将每个 getter
函数都转成一个计算属性。
总结
defineStore
返回一个 useStore
函数,通过执行 useStore
能够获取对应的 store
。调用useStore
时咱们并没有传入 id
,为什么能精确获取store
呢?这是因为 useStore
是个闭包,在执行 useStore
执行过程中会主动获取id
。
获取 store
的过程:
- 首先获取组件实例
- 应用
inject(piniaSymbol)
获取pinia
实例 - 判断
pinia._s
中是否有对应id
的键,如果有间接取对应的值作为store
,如果没有则创立store
store
创立流程分两种:setup
形式与 options
形式
setup
形式:
- 首先在
pinia.state.value
中增加键为$id
的空对象,以便后续赋值 - 应用
reactive
申明一个响应式对象store
- 将
store
存至pinia._s
中 - 执行
setup
获取返回值setupStore
- 遍历
setupStore
的键值,如果值是ref
(不是computed
)或reactive
,将键值增加到pinia.state.value[$id]
中;如果值时function
,首先将值应用wrapAction
包装,而后用包装后的function
替换setupStore
中对应的值 - 将
setupStore
合并到store
中 - 拦挡
store.$state
,使get
操作能够正确获取pinia.state.value[$id]
,set
操作应用this.$patch
更新 - 调用
pinia._p
中的扩大函数,扩大store
options
形式:
- 从
options
中提取state
、getter
、actions
- 构建
setup
函数,在setup
函数中会将getter
解决成计算属性 - 应用
setup
形式创立store
- 重写
store.$reset