• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • actions

    Action 相当于组件中的method。它们可以通过defineStore()中的actions属性来定义,并且它们也是定义业务逻辑的完美选择。

    export const useStore = defineStore('main', {
      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())
        },
      },
    })
    

    类似 getter,action 也可通过this访问整个 store 实例,并支持完整的类型标注(以及自动补全)。不同的是,action 可以是异步的,你可以在它们里面使用await调用任何 API,以及其他 action!下面是一个使用 Mande 的例子。请注意,你使用什么库并不重要,只要你得到的是一个Promise,你甚至可以(在浏览器中)使用原生fetch函数:

    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)
            // 让表单组件显示错误
            return error
          }
        },
      },
    })
    


    想要使用另一个 store 的话,那你直接在 action 中调用:

    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')
          }
        },
      },
    })
    


    Composition API 组件中,调用 action

    当组件是 Composition API 编程方式时,在setup()中,将任何 action 作为 store 的一个方法直接调用:

    <script>
    export default {
      setup() {
        const store = useCounterStore()
        store.randomizeCounter()
      },
    }
    </script>
    
    <script setup>
        const store = useCounterStore()
        store.randomizeCounter()
    </script>
    

    你也完全可以自由地设置,任何你想要的参数以及返回任何结果。当调用 action 时,一切类型也都是可以被自动推断出来的。

    在组件使用中,调用store 中 useCounterStore 的 actions
    <script>
    export default defineComponent({
      setup() {
        const store = useCounterStore()
        // call the action as a method of the store
        store.randomizeCounter()
        return {}
      },
    })
    </script>
    



    Options API 组件中,调用 action

    在下面的例子中,你可以假设相关的 store 已经创建了

    // Example File Path:
    // ./src/stores/counter.js
    
    <script>
    import { defineStore } from 'pinia',
    
    export const useCounterStore = defineStore('counter', {
      state: () => ({
        count: 0
      }),
      actions: {
        increment() {
          this.count++
        }
      }
    })
    </script>
    

    当组件是 Options API 编程方式时,在setup()钩子中,直接调用使用 Pinia,然后在methods中,直接调用 action。

    <script>
    import { useCounterStore } from '../stores/counter'
    
    export default {
      setup() {
        const counterStore = useCounterStore()
        return { counterStore }
      },
      methods: {
        incrementAndPrint() {
          this.counterStore.increment()
          console.log('New Count:', this.counterStore.count)
        },
      },
    }
    </script>
    

    当组件是 Options API 编程方式时,没有使用setup()钩子调用 Pinia,那么在methods中,调用 action,可以使用mapActions()辅助函数,将 action 属性映射为组件中的方法:

    <script>
    import { mapActions } from 'pinia'
    import { useCounterStore } from '../stores/counter'
    
    export default {
      methods: {
        // 访问组件内的 this.increment()
        // 与从 store.increment() 调用相同
        ...mapActions(useCounterStore, ['increment'])
        // 与上述相同,但将其注册为this.myOwnName()
        ...mapActions(useCounterStore, { myOwnName: 'increment' }),
      },
    }
    </script>
    


    订阅 action

    可以使用store.$onAction()来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。after表示在 promise 解决之后,允许你在 action 解决后执行一个一个回调函数。同样地,onError允许你在 action 抛出错误或 reject 时执行一个回调函数。这些函数对于追踪运行时错误非常有用,类似于Vue docs 中的这个提示。

    这里有一个例子,在运行 action 之前以及 action resolve/reject 之后打印日志记录。

    const unsubscribe = someStore.$onAction(
      ({
        name, // action 名称
        store, // store 实例,类似 `someStore`
        args, // 传递给 action 的参数数组
        after, // 在 action 返回或解决后的钩子
        onError, // action 抛出或拒绝的钩子
      }) => {
        // 为这个特定的 action 调用提供一个共享变量
        const startTime = Date.now()
        // 这将在执行 "store "的 action 之前触发。
        console.log(`Start "${name}" with params [${args.join(', ')}].`)
    
        // 这将在 action 成功并完全运行后触发.
        // 它等待着任何返回的 promise
        after((result) => {
          console.log(
            `Finished "${name}" after ${
              Date.now() - startTime
            }ms.\nResult: ${result}.`
          )
        })
    
        // 如果 action 抛出或返回一个拒绝的 promise,这将触发
        onError((error) => {
          console.warn(
            `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
          )
        })
      }
    )
    
    // 手动删除监听器
    unsubscribe()
    

    默认情况下,action 订阅器会被绑定到添加它们的组件上(如果 store 位于组件的setup()中)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将true作为第二个参数传递给 action 订阅器,以便将其从当前组件中分离:

    export default {
      setup() {
        const someStore = useSomeStore()
        // 在组件被卸载后,这个订阅依旧会被保留
        someStore.$onAction(callback, true)
    
        // ...
      },
    }
    

    上篇:getters

    下篇:Plugins(插件)