You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Pinia Store状态中存储函数?Vue3自定义弹窗实现遇阻

Vue3 + Pinia 自定义Alert组件报错:resolve/reject is not a function

问题描述

在Vue3项目中使用Pinia实现全局可调用的自定义Alert组件,将Promise的resolve/reject函数存入Pinia的state中,调用时出现resolve/reject is not a function错误,控制台打印发现保存的是代理对象而非原始函数。此前用Vuex在Vue2中可正常实现。

相关代码

store.js

import { defineStore } from 'pinia'

export const useAppStore = defineStore('config', {
  state: () => ({
    alert: {
      data: {
        show: false,
        title: '',
        message: ''
      },
      config: {
        resolve: () => {},
        reject: () => {}
      }
    }
  }),
  actions: {
    showAlert(title, message) {
      return new Promise((resolve, reject) => {
        this.alert.data.show = true
        this.alert.data.title = title
        this.alert.data.message = message

        this.alert.config.resolve = resolve
        this.alert.config.reject = reject
      })
    },
    hideAlert(payload = null) {
      this.alert.data.show = false
      if (payload === false) {
        this.alert.config.reject(payload)
      } else {
        this.alert.config.resolve(payload)
      }

      this.alert.config.resolve = () => {}
      this.alert.config.reject = () => {}
    }
  }, 
  getters: {
    alertData: s => s.alert.data
  },
})

App.vue

<template>
    <!-- layout, router view, etc -->

    <div class="alert-bg" v-if="alertData.show">
        <div class="alert-box" @click-outside="appStore.hideAlert(false)">
            <h2 class="alert-title">
                {{ alertData.title }}
            </h2>
            <p class="alert-message">
                {{ alertData.message }}
            </p>
            <button @click="appStore.hideAlert()">
                OK
            </button>
        </div>
    </div>
</template>
<script setup>
    import { useAppStore } from 'store.js'
    const appStore = useAppStore()

    const alertData = appStore.alertData
</script>

random-component.vue

<template>
  <form @submit.prevent="submitForm">
   <!-- form elemets -->
  </form>
</template>

<script setup>
    import { useAppStore } from 'store.js'
    const appStore = useAppStore()

    const submitForm = () => {
       callBackend.then(response => {
          if(response.success) {
             appStore.showAlert(
                'Success!',
                'Continue to home page?'
             ).then(() => {
                // router.push('/home')
             }).catch(() => {
                // clear the form
             })
          }
       })
    }

</script>

解决方案

问题根源

Pinia会对state中的所有数据进行响应式代理,包括函数。当你把Promise的resolve/reject存入state时,它们会被包装成Vue的响应式代理对象,导致调用时触发is not a function错误。

修改后的store.js

将resolve/reject函数存储在Pinia实例的非响应式属性中,避免被响应式系统包装:

import { defineStore } from 'pinia'

export const useAppStore = defineStore('config', {
  state: () => ({
    alert: {
      data: {
        show: false,
        title: '',
        message: ''
      }
      // 移除state中的config字段,不再存储函数
    }
  }),
  actions: {
    showAlert(title, message) {
      return new Promise((resolve, reject) => {
        this.alert.data.show = true
        this.alert.data.title = title
        this.alert.data.message = message
        // 将resolve/reject存入store实例的自定义属性(前缀下划线标识内部属性)
        this._alertResolve = resolve
        this._alertReject = reject
      })
    },
    hideAlert(payload = null) {
      this.alert.data.show = false
      // 使用可选链避免未初始化时调用报错
      if (payload === false) {
        this._alertReject?.(payload)
      } else {
        this._alertResolve?.(payload)
      }
      // 清空引用,防止内存泄漏
      this._alertResolve = null
      this._alertReject = null
    }
  }, 
  getters: {
    alertData: s => s.alert.data
  },
})

说明

  • 移除state中存储函数的alert.config字段,仅保留需要响应式的alert数据。
  • 将resolve/reject直接挂载到store实例的_alertResolve和_alertReject属性上,这些属性不会被Pinia做响应式处理,可正常调用。
  • 使用?.可选链操作符,避免在alert未初始化时调用resolve/reject导致报错。
  • 调用后清空函数引用,防止内存泄漏。

内容的提问来源于stack exchange,提问作者Nizar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 22:32:44