关于前端:pinia源码二defineStore源码解析

前言

【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函数能够接管三个参数:idOrOptionssetupsetOptions,后两个参数为可选参数。上面是三个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中申明了三个变量:idoptionsisSetupStore,其中id为定义的store的惟一idoptions为定义store时的optionsisSetupStore代表传入的setup是不是个函数。

而后依据传入的idOrOptions的类型,为idotions赋值。紧接着申明了一个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接管两个可选参数:piniahotpinia是个Pinia的实例,而hot只在开发环境下有用,它与模块的热更新无关。

useStore中会首先获取以后组件实例,如果存在组件实例,应用inject(piniaSymbol)获取pinia(在install中会进行provide),并将其设置为activePinia,而后在activePinia._s中查找是否有被注册为idstore,如果没有则创立store,将其注册到activePinia._s中。最初返回activePinia._sid对应的store

当初咱们晓得useStore函数,最终会返回一个store。那么这个store是什么呢?它是如何创立的呢?在useStore中依据不同状况中有两中形式来创立store,别离是:createSetupStorecreateOptionsStore。这两个形式的应用条件是:如果defineStore第二个参数是个function调用createSetupStore,相同调用createOptionsStore

createSetupStore

createSetupStore函数代码过长,这里就不贴残缺代码了。createSetupStore可接管参数如下:

参数 阐明
$id 定义storeid
setup 一个能够返回state的函数
options defineStoreoptions
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的属性:如果propkey对应的值)为ref(不为computed)或reactive,则将keyprop同步到pina.state.value[$id]中;如果propfunction,则会应用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增加到afterCallbackListonErrorCallbackList中。例如:

store.$onAction(({ after, onError, name, store }) => {
  after((value) => {
    console.log(value)
  })
  
  onError((error) => {
    console.log(error)
  })
})

遍历完setupStore之后,会将setupStore合并至storestore的原始对对象中,以方便使用storeToRefs()检索响应式对象。

if (isVue2) {
  Object.keys(setupStore).forEach((key) => {
    set(
      store,
      key,
      setupStore[key]
    )
  })
} else {
  assign(store, setupStore)
  assign(toRaw(store), setupStore)
}

紧接着拦挡store.$stategetset办法:当调用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中增加回调函数。回调接管一个对象参数:该对象蕴含nameactionkey值)、store(以后store)、after(增加action执行完之后的回调)、onError(增加action执行过程中的谬误回调)、argsaction的参数)属性。

示例:

// 统计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(订阅列表)、argsaction的参数列表)

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,这个触发过程中会将afterCallbackonErrorCallback增加到对应列表。而后调用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 objectstate重置为初始状态。只在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}detachedaddSubscription中的detachedflush代表是否同步触发回调,可取值: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,它有三个值:

  1. MutationType.direct:通过state.name='xxx'/store.$state.name='xxx'等形式批改
  2. MutationType.patchObject:通过store.$patch({ name: 'xxx' })形式批改
  3. 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 定义storeid
options defineStoreoptions
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,最初将解决后的gettersactions都合并到localState中,将其返回。对于getters的解决:将每个getter函数都转成一个计算属性。

总结

defineStore返回一个useStore函数,通过执行useStore能够获取对应的store。调用useStore时咱们并没有传入id,为什么能精确获取store呢?这是因为useStore是个闭包,在执行useStore执行过程中会主动获取id

获取store的过程:

  1. 首先获取组件实例
  2. 应用inject(piniaSymbol)获取pinia实例
  3. 判断pinia._s中是否有对应id的键,如果有间接取对应的值作为store,如果没有则创立store

store创立流程分两种:setup形式与options形式

setup形式:

  1. 首先在pinia.state.value中增加键为$id的空对象,以便后续赋值
  2. 应用reactive申明一个响应式对象store
  3. store存至pinia._s
  4. 执行setup获取返回值setupStore
  5. 遍历setupStore的键值,如果值是ref(不是computed)或reactive,将键值增加到pinia.state.value[$id]中;如果值时function,首先将值应用wrapAction包装,而后用包装后的function替换setupStore中对应的值
  6. setupStore合并到store
  7. 拦挡store.$state,使get操作能够正确获取pinia.state.value[$id]set操作应用this.$patch更新
  8. 调用pinia._p中的扩大函数,扩大store

options形式:

  1. options中提取stategetteractions
  2. 构建setup函数,在setup函数中会将getter解决成计算属性
  3. 应用setup形式创立store
  4. 重写store.$reset

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理