跳至内容

操作

操作相当于组件中的 方法。它们可以在 defineStore() 中使用 actions 属性定义,并且非常适合定义业务逻辑

js
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    // since we rely on `this`, we cannot use an arrow function
    increment() {
      this.count++
    },
    randomizeCounter() {
      this.count = Math.round(100 * Math.random())
    },
  },
})

获取器 一样,操作可以通过 this 访问整个商店实例,并提供完整的类型(以及自动完成 ✨)支持与获取器不同,actions 可以是异步的,您可以在操作中 await 任何 API 调用,甚至其他操作!以下是一个使用 Mande 的示例。请注意,只要您获得 Promise,使用的库并不重要。您甚至可以使用本机 fetch 函数(仅限浏览器)

js
import { mande } from 'mande'

const api = mande('/api/users')

export const useUsers = defineStore('users', {
  state: () => ({
    userData: null,
    // ...
  }),

  actions: {
    async registerUser(login, password) {
      try {
        this.userData = await api.post({ login, password })
        showTooltip(`Welcome back ${this.userData.name}!`)
      } catch (error) {
        showTooltip(error)
        // let the form component display the error
        return error
      }
    },
  },
})

您也可以自由设置任何您想要的参数并返回任何内容。调用操作时,所有内容都会自动推断!

操作像普通函数和方法一样被调用

vue
<script setup>
const store = useCounterStore()
// call the action as a method of the store
store.randomizeCounter()
</script>

<template>
  <!-- Even on the template -->
  <button @click="store.randomizeCounter()">Randomize</button>
</template>

访问其他商店的操作

要使用另一个商店,您可以在操作中直接使用它

js
import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    preferences: null,
    // ...
  }),
  actions: {
    async fetchUserPreferences() {
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated')
      }
    },
  },
})

与选项 API 一起使用

对于以下示例,您可以假设创建了以下商店

js
// Example File Path:
// ./src/stores/counter.js

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() {
      this.count++
    },
  },
})

使用 setup()

虽然 Composition API 并不适合所有人,但 setup() 钩子可以使 Pinia 在使用选项 API 时更容易使用。无需额外的映射辅助函数!

vue
<script>
import { useCounterStore } from '../stores/counter'

export default defineComponent({
  setup() {
    const counterStore = useCounterStore()

    return { counterStore }
  },
  methods: {
    incrementAndPrint() {
      this.counterStore.increment()
      console.log('New Count:', this.counterStore.count)
    },
  },
})
</script>

不使用 setup()

如果您希望完全不使用 Composition API,可以使用 mapActions() 辅助函数将操作属性映射为组件中的方法

js
import { mapActions } from 'pinia'
import { useCounterStore } from '../stores/counter'

export default {
  methods: {
    // gives access to this.increment() inside the component
    // same as calling from store.increment()
    ...mapActions(useCounterStore, ['increment']),
    // same as above but registers it as this.myOwnName()
    ...mapActions(useCounterStore, { myOwnName: 'increment' }),
  },
}

订阅操作

可以使用 store.$onAction() 观察操作及其结果。传递给它的回调在操作本身之前执行。after 处理 promise 并允许您在操作解析后执行函数。类似地,onError 允许您在操作抛出或拒绝时执行函数。这些对于在运行时跟踪错误很有用,类似于 Vue 文档中的此提示

以下是一个在运行操作之前和操作解析/拒绝之后记录日志的示例。

js
const unsubscribe = someStore.$onAction(
  ({
    name, // name of the action
    store, // store instance, same as `someStore`
    args, // array of parameters passed to the action
    after, // hook after the action returns or resolves
    onError, // hook if the action throws or rejects
  }) => {
    // a shared variable for this specific action call
    const startTime = Date.now()
    // this will trigger before an action on `store` is executed
    console.log(`Start "${name}" with params [${args.join(', ')}].`)

    // this will trigger if the action succeeds and after it has fully run.
    // it waits for any returned promised
    after((result) => {
      console.log(
        `Finished "${name}" after ${
          Date.now() - startTime
        }ms.\nResult: ${result}.`
      )
    })

    // this will trigger if the action throws or returns a promise that rejects
    onError((error) => {
      console.warn(
        `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
      )
    })
  }
)

// manually remove the listener
unsubscribe()

默认情况下,操作订阅绑定到添加它们的组件(如果商店位于组件的 setup() 中)。这意味着,当组件卸载时,它们将自动删除。如果您还想在组件卸载后保留它们,请将 true 作为第二个参数传递以将操作订阅从当前组件分离

vue
<script setup>
const someStore = useSomeStore()

// this subscription will be kept even after the component is unmounted
someStore.$onAction(callback, true)
</script>