百科狗-知识改变命运!
--

Plugins(插件) - Pinia(状态管理)

是丫丫呀1年前 (2023-11-21)阅读数 20#技术干货
文章标签属性

Plugins(插件)

虽然是轻量级 API,Pania 可以完全扩展。以下是您可以做的事情列表:

  • 向 store 添加新属性
  • 定义 store 时添加新选项
  • 向 store 添加新方法
  • 包装现有方法
  • 更改甚至取消 action
  • 实现本地存储这样的副作用
  • 仅适用于特定 store

通过pinia.use(),把插件添加到 Pinia 实例中。最简单的示例是通过返回对象向所有 store 添加静态属性:

import { createPinia } from 'pinia'

// add a property named `secret` to every store that is created after this plugin is installed
// this could be in a different file
function SecretPiniaPlugin() {
  return { secret: 'the cake is a lie' }
}

const pinia = createPinia()
// give the plugin to pinia
pinia.use(SecretPiniaPlugin)

// in another file
const store = useStore()
store.secret // 'the cake is a lie'

这对于添加全局对象(如 router、modal 或 toast 管理器)很有用。

Pinia 插件是一个函数,可以选择返回要添加到 store 的属性。它需要一个可选参数context:

export function myPiniaPlugin(context) {
  context.pinia // the pinia created with `createPinia()`
  context.app // the current app created with `createApp()` (Vue 3 only)
  context.store // the store the plugin is augmenting
  context.options // the options object defining the store passed to `defineStore()`
  // ...
}

然后通过pinia.use()函数传递给 Pinia:

pinia.use(myPiniaPlugin)

插件仅适用于传递给应用后创建 Pinia 的 store,否则将不会被应用。


扩充 store

您可以通过简单地在插件中返回它们的对象来为每个商店添加属性:

pinia.use(() => ({ hello: 'world' }))

您也可以直接在 store 上设置属性,但如果可能,请使用返回版本,以便它们可以被 devtools 自动跟踪:

pinia.use(({ store }) => {
  store.hello = 'world'
})

插件返回的任何属性都将由 devtools 自动跟踪,因此为了hello在 devtools 中可见,请确保将其添加到存储中。仅当您希望在 devtools 中调试时,才在 dev 模式下使用store._customProperties

// from the example above
pinia.use(({ store }) => {
  store.hello = 'world'
  // make sure your bundler handle this. webpack and vite should do it by default
  if (process.env.NODE_ENV === 'development') {
    // add any keys you set on the store store._customProperties.add('hello')
  }
})

请注意,每个 store 都用reactive响应式包装的,自动解包时,它包含的任何 Ref(ref(),computed(),...):

const sharedRef = ref('shared')
pinia.use(({ store }) => {
  // each store has its individual `hello` property
  store.hello = ref('secret')
  // it gets automatically unwrapped
  store.hello // 'secret'

  // all stores are sharing the value `shared` property
  store.shared = sharedRef
  store.shared // 'shared'
})

这就是为什么,您可以在没有.value的情况下,可以访问所有计算属性,以及它们是响应性的原因。


添加新 state

如果您想将新的 state 属性添加到store,或打算在实例化期间使用的属性,您必须在两个地方添加它:

  • 在 store 中,您可以使用store.myState访问它。
  • 在store.$state,以便在开发工具中使用,并在 SSR 期间序列化。
  • 除此之外,您肯定必须使用ref()(或其他响应式 API)才能在不同的访问中共享值:
import { toRef, ref } from 'vue'

pinia.use(({ store }) => {
  // to correctly handle SSR, we need to make sure we are not overriding an
  // existing value
  if (!Object.prototype.hasOwnProperty(store.$state, 'hasError')) {
    // hasError is defined within the plugin, so each store has their individual
    // state property
    const hasError = ref(false)
    // setting the variable on `$state`, allows it be serialized during SSR
    store.$state.hasError = hasError
  }
  // we need to transfer the ref from the state to the store, this way
  // both accesses: store.hasError and store.$state.hasError will work
  // and share the same variable
  // See https://vuejs.org/api/reactivity-utilities.html#toref
  store.hasError = toRef(store.$state, 'hasError')

  // in this case it's better not to return `hasError` since it
  // will be displayed in the `state` section in the devtools
  // anyway and if we return it, devtools will display it twice.
})

请注意,插件中发生的状态更改或添加(包括调用store.$patch())发生在 store 处于活动状态之前,因此不会触发任何订阅。

警告:如果您使用的是 Vue 2,Pinia 会受到与 Vue 相同的响应性警告。在创建新的 state 属性时,您需要使用Vue.set()(Vue 2.7)或set()(from @vue/composition-apifor Vue > 2.7)secret 和 hasError

import { set, toRef } from '@vue/composition-api'
pinia.use(({ store }) => {
  if (!Object.prototype.hasOwnProperty(store.$state, 'hello')) {
    const secretRef = ref('secret')
    // If the data is meant to be used during SSR, you should
    // set it on the `$state` property so it is serialized and
    // picked up during hydration
    set(store.$state, 'secret', secretRef)
  }
  // set it directly on the store too so you can access it
  // both ways: `store.$state.secret` / `store.secret`
  set(store, 'secret', toRef(store.$state, 'secret'))
  store.secret // 'secret'
})


添加新的外部属性

当添加外部属性、来自其他库的类实例或仅仅是非响应性的东西时,您应该在将对象markRaw()传递给 Pinia 之前将其包装起来。下面一个将 router 添加到每个 store 的示例:

import { markRaw } from 'vue'
// adapt this based on where your router is
import { router } from './router'

pinia.use(({ store }) => {
  store.router = markRaw(router)
})


在插件内部调用$subscribe

您也可以在插件中使用store.$subscribestore.$onAction

pinia.use(({ store }) => {
  store.$subscribe(() => {
    // react to store changes
  })
  store.$onAction(() => {
    // react to store actions
  })
})


添加新选项

可以在定义 store 时创建新选项,以便以后从插件中使用它们。例如,您可以创建去抖动的debounce选项,允许您对任何 action 进行操作:

defineStore('search', { actions: {
    searchContacts() {
      // ...
    },
  },

  // this will be read by a plugin later on debounce: {
    // debounce the action searchContacts by 300ms
    searchContacts: 300,
  },
})

然后插件可以读取该选项,以包装操作并替换原始操作:

// use any debounce library
import debounce from 'lodash/debounce'

pinia.use(({ options, store }) => {
  if (options.debounce) {
    // we are overriding the actions with new ones
    return Object.keys(options.debounce).reduce((debouncedActions, action) => {
      debouncedActions[action] = debounce(
        store[action],
        options.debounce[action]
      )
      return debouncedActions
    }, {})
  }
})

请注意,使用设置语法时,自定义选项作为第三个参数传递:

defineStore(
  'search',
  () => {
    // ...
  },
  {
    // this will be read by a plugin later on
    debounce: {
      // debounce the action searchContacts by 300ms
      searchContacts: 300,
    },
  }
)


TypeScript

上面显示的所有内容都可以通过键入支持来完成,因此您无需使用any@ts-ignore.

Typing 插件

Pinia 插件可以按如下方式键入:

import { PiniaPluginContext } from 'pinia'

export function myPiniaPlugin(context: PiniaPluginContext) {
  // ...
}


键入新的 state 属性

向 store 添加新属性时,您还应该扩展PiniaCustomProperties接口。

import 'pinia'

declare module 'pinia' {
  export interface PiniaCustomProperties {
    // by using a setter we can allow both strings and refs
    set hello(value: string | Ref)
    get hello(): string

    // you can define simpler values too
    simpleNumber: number
  }
}

Plugins(插件) - Pinia(状态管理)

然后可以安全地写入和读取它:

pinia.use(({ store }) => {
  store.hello = 'Hola'
  store.hello = ref('Hola')

  store.simpleNumber = Math.random()
  // @ts-expect-error: we haven't typed this correctly
  store.simpleNumber = ref(Math.random())
})

PiniaCustomProperties是一种通用类型,允许您引用 store 的属性。想象以下示例,我们将初始选项复制为$options(这仅适用于选项存储):

pinia.use(({ options }) => ({ $options: options }))

我们可以通过使用 4 种通用类型来正确输入PiniaCustomProperties

import 'pinia'

declare module 'pinia' {
  export interface PiniaCustomProperties {
    $options: {
      id: Id
      state?: () => S
      getters?: G
      actions?: A
    }
  }
}


在泛型中扩展类型时,它们的命名必须与源代码中的完全相同。Id 不能命名为 id 或 I,S 不能命名为 State。以下是每个字母的含义:

  • S: State
  • G: Getters
  • A: Actions
  • SS: Setup Store / Store


输入新 state

当添加新的state 属性(store和store.$state)时,您需要添加类型来PiniaCustomStateProperties代替。与PiniaCustomProperties不同的是,它只接收State泛型:

import 'pinia'

declare module 'pinia' {
  export interface PiniaCustomStateProperties {
    hello: string
  }
}


键入新的创建选项

在为defineStore()创建新选项时,您应该扩展DefineStoreOptionsBase。与PiniaCustomProperties不同的是,它只公开了两个泛型:State和Store类型,允许您限制可以定义的内容。例如,您可以使用操作的名称:

import 'pinia'

declare module 'pinia' {
  export interface DefineStoreOptionsBase {
    // allow defining a number of ms for any of the actions
    debounce?: Partial
  }
}

还有一个StoreGetters类型用于从存储类型中提取 getter。您还可以仅通过分别扩展类型DefineStoreOptions和definestetupstoreoptions来扩展设置存储或选项存储的选项。


Nuxt.js

在 Nuxt 旁边使用 Pinia 时,您必须先创建一个 Nuxt 插件。这将使您可以访问该 Pinia 实例:

// plugins/myPiniaPlugin.js
import { PiniaPluginContext } from 'pinia'
import { Plugin } from '@nuxt/types'

function MyPiniaPlugin({ store }: PiniaPluginContext) {
  store.$subscribe((mutation) => {
    // react to store changes
    console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)
  })

  // Note this has to be typed if you are using TS
  return { creationTime: new Date() }
}

const myPlugin: Plugin = ({ $pinia }) => {
  $pinia.use(MyPiniaPlugin)
}

export default myPlugin

请注意,上面的示例使用的是 TypeScript,如果您使用的是文件,则必须删除类型注释PiniaPluginContext及其导入Plugin.js

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)