如何在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
相关产品推荐
相关产品推荐

