Pinia插件的使用(简易版数据持久化插件)

插件

由于有了底层 API 的支持,Pinia store 现在完全支持扩展。以下是你可以扩展的内容:

  • 为 store 添加新的属性
  • 定义 store 时增加新的选项
  • 为 store 增加新的方法
  • 包装现有的方法
  • 改变甚至取消 action
  • 实现副作用,如本地存储
  • 应用插件于特定 store

插件是通过 pinia.use() 添加到 pinia 实例的。最简单的例子是通过返回一个对象将一个静态属性添加到所有 store。

import { createPinia } from 'pinia'

// 创建的每个 store 中都会添加一个名为 `secret` 的属性。
// 在安装此插件后,插件可以保存在不同的文件中
function SecretPiniaPlugin() {
  return { secret: 'the cake is a lie' }
}

const pinia = createPinia()
// 将该插件交给 Pinia
pinia.use(SecretPiniaPlugin)

// 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'

这对添加全局对象很有用,如路由器、modal 或 toast 管理器。

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

export function myPiniaPlugin(context) {
  context.pinia // 用 `createPinia()` 创建的 pinia。 
  context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
  context.store // 该插件想扩展的 store
  context.options // 定义传给 `defineStore()` 的 store 的可选对象。
  // ...
}

然后用 pinia.use() 将这个函数传给 pinia

插件只会应用于在 pinia 传递给应用后创建的 store,否则它们不会生效。

扩展 Store

你可以直接通过在一个插件中返回包含特定属性的对象来为每个 store 都添加上特定属性:

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

你也可以直接在 store 上设置该属性,但可以的话,请使用返回对象的方法,这样它们就能被 devtools 自动追踪到

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

 值得注意的是,每个 store 都被 reactive包装过,所以可以自动解包任何它所包含的 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 的情况下你依旧可以访问所有计算属性的原因,也是它们为什么是响应式的原因。

添加新的 state

如果你想给 store 添加新的 state 属性或者在服务端渲染的激活过程中使用的属性,你必须同时在两个地方添加它。。

  • 在 store 上,然后你才可以用 store.myState 访问它。
  • 在 store.$state 上,然后你才可以在 devtools 中使用它,并且,在 SSR 时被正确序列化(serialized)

需要注意的是,在一个插件中, state 变更或添加(包括调用 store.$patch())都是发生在 store 被激活之前,因此不会触发任何订阅函数

如果你使用的是 Vue 2,Pinia 与 Vue 一样,受限于相同的响应式限制。在创建新的 state 属性时,如 secret 和 hasError,你需要使用 Vue.set() (Vue 2.7) 或者 @vue/composition-api 的 set() (Vue < 2.7)。

import { set, toRef } from '@vue/composition-api'
pinia.use(({ store }) => {
  if (!Object.prototype.hasOwnProperty(store.$state, 'hello')) {
    const secretRef = ref('secret')
    // 如果这些数据是要在 SSR 过程中使用的
    // 你应该将其设置在 `$state' 属性上
    // 这样它就会被序列化并在激活过程中被接收
    set(store.$state, 'secret', secretRef)
    // 直接在 store 里设置,这样你就可以访问它了。
    // 两种方式都可以:`store.$state.secret` / `store.secret`。
    set(store, 'secret', secretRef)
    store.secret // 'secret'
  }
})

添加新的外部属性

当添加外部属性、第三方库的类实例或非响应式的简单值时,你应该先用 markRaw() 来包装一下它,再将它传给 pinia。下面是一个在每个 store 中添加路由器的例子:

import { markRaw } from 'vue'
// 根据你的路由器的位置来调整
import { router } from './router'

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

在插件中调用 $subscribe

你也可以在插件中使用 store.$subscribe 和 store.$onAction 。

pinia.use(({ store }) => {
  store.$subscribe(() => {
    // 响应 store 变化
  })
  store.$onAction(() => {
    // 响应 store actions
  })
})

添加新的选项

在定义 store 时,可以创建新的选项,以便在插件中使用它们。例如,你可以创建一个 debounce 选项,允许你让任何 action 实现防抖。

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

  // 这将在后面被一个插件读取
  debounce: {
    // 让 action searchContacts 防抖 300ms
    searchContacts: 300,
  },
})

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

// 使用任意防抖库
import debounce from 'lodash/debounce'

pinia.use(({ options, store }) => {
  if (options.debounce) {
    // 我们正在用新的 action 来覆盖这些 action
    return Object.keys(options.debounce).reduce((debouncedActions, action) => {
      debouncedActions[action] = debounce(
        store[action],
        options.debounce[action]
      )
      return debouncedActions
    }, {})
  }
})

注意,在使用 setup 语法时,自定义选项作为第 3 个参数传递:

defineStore(
  'search',
  () => {
    // ...
  },
  {
    // 这将在后面被一个插件读取
    debounce: {
      // 让 action searchContacts 防抖 300ms
      searchContacts: 300,
    },
  }
)

自定义数据持久化插件TS:

import { PiniaPluginContext } from "pinia";

export function persistedState(context: PiniaPluginContext) {
    // 根据模块名,拿到对应localStorage
    const currentState = JSON.parse(
        localStorage.getItem(context.store.$id) || "{}"
    )

    // 把数据存放到pinia对应的模块里
    context.store.$patch(currentState)
    /**
     * 每次state发生变化时,都把它保存在localStorage里
     * 参数一:当前修改store的上下文,可以通过它获取_store.storeId充当键
     * 参数二:当前修改的状态,可以充当值
     */
    context.store.$subscribe((_store, state) => {
        console.log(_store)
        console.log(state)
        // 存入键值(依据上图数据结构)(需要序列化)
        localStorage.setItem(_store.storeId, JSON.stringify(state))
    }, {
        // 组件卸载,依赖仍然存在
        detached: true
    })
}

在 index.ts 中导入 persistedState,然后通过 pinia 使用它 

import { createPinia } from 'pinia'
import { persistedState } from './plugins/persistedState'

const pinia = createPinia()

// 使用插件
pinia.use(persistedState)

export default pinia

  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值