Nuxt.js自定义Alert组件重复触发相同错误时不显示问题求助
Nuxt.js自定义Alert组件重复触发不显示问题
我用Nuxt.js(Vue.js)开发网站,做了个联系表单,用户输入错误或未填必填项时会触发自定义通知,但重复触发相同错误(比如手机号格式错误)时,Alert组件第二次不显示,折腾两天了,求帮忙找原因。
WarningAlert组件代码
<!-- WarningAlert.vue --> <template> <div v-if="show" class="overlay"> <div class="alert alert-warning" role="alert"> <div class="container"> <div class="alert-icon"> <i class="now-ui-icons ui-1_bell-53"></i> </div> <strong>{{ strongText }}</strong> {{ message }} <button v-if="dismissible" type="button" class="close" @click="hideOverlay" aria-label="Close"> <span aria-hidden="true"> <i class="now-ui-icons ui-1_simple-remove"></i> </span> </button> </div> </div> </div> </template> <script> export default { props: { message: String, type: String, strongText: String, dismissible: Boolean, }, data() { return { show: false, }; }, methods: { hideOverlay() { this.show = false; }, showWarningAlert(message) { this.message = message; this.show = true; setTimeout(() => { this.hideOverlay(); }, 5000); }, hideWarningAlert() { this.show = false; this.message = ''; }, }, watch: { message() { this.show = true; setTimeout(() => { this.hideOverlay(); }, 5000); }, }, mounted() { this.showWarningAlert(this.message); }, }; </script> <style scoped> .overlay { position: fixed; top: 50px; left: 0; width: 100%; z-index: 1000; display: flex; justify-content: center; align-items: flex-start; padding: 20px; } .alert { width: 70%; } </style>
联系表单及组件导入代码
<!-- Alert component --> <WarningAlert v-if="showWarningAlert" :message="warningMessage" :type="'warning'" :strongText="'Warning!'" :dismissible="true" @dismiss="dismissWarningAlert"></WarningAlert> <!-- Contact Form --> <div class="main"> <div class="contact-content"> <div class="container"> <div class="row"> <div class="col-md-5 ml-auto mr-auto"> <h2 class="title">Senden Sie uns eine Nachricht</h2> <p class="description">Wir stehen Ihnen gerne zur Verfügung. Wir werden Ihnen innerhalb eines Werktages antworten. <br> <br> </p> <form role="form" id="contact-form" @submit.prevent="submitForm" method="post"> <label>Ihr Name</label> <fg-input required placeholder="Name..." v-model="form.firstName" addon-left-icon="now-ui-icons users_circle-08"></fg-input> <label>Email Adresse</label> <fg-input required placeholder="Email..." v-model="form.email" addon-left-icon="now-ui-icons users_circle-08" title="Please enter a valid email address"></fg-input> <label>Telefon</label> <fg-input required placeholder="Telefonnummer..." v-model="form.phone" addon-left-icon="now-ui-icons tech_mobile"></fg-input> <div class="form-group"> <label>Ihre Nachricht</label> <textarea name="message" class="form-control" id="message" placeholder="Nachricht..." v-model="form.message" rows="6"></textarea> </div> <div class="submit text-center"> <button class="btn btn-success btn-rounded" type="submit" round>Senden</button> </div> </form>
submitForm方法代码
methods: { async submitForm() { // Initialisation this.showWarningAlert = false; this.warningMessage = ''; // Check if all relevant fields filled out if (!this.form.firstName || !this.form.email || !this.form.phone) { this.showWarningAlert = true; this.warningMessage = 'Please fill out all required fields (Name, Email, Phone).'; return; } const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; if (!this.form.email.match(emailRegex)) { this.showWarningAlert = true; this.warningMessage = 'Please enter a valid email address.'; return; } const phoneRegex = /^(\+\d{1,3})?\d{1,14}$/; if (!this.form.phone.match(phoneRegex)) { this.showWarningAlert = true; this.warningMessage = 'Please enter a valid phone number.'; return; } // Send formulas if everything is validated try { const response = await axios.post('http://localhost:3005/send-email', this.form); if (response.status === 200) { console.log('Email sent successfully'); } } catch (error) { console.error('Error sending email:', error); // Show warning if error this.showWarningAlert = true; this.warningMessage = 'Something went wrong. Please contact us via email.'; } finally { // Reset Warning properties before leaving the method this.showWarningAlert = false; this.warningMessage = ''; } }, dismissWarningAlert() { // Dismiss the warning alert this.showWarningAlert = false; this.warningMessage = ''; } },
问题根源与修复方案
核心问题
- 组件销毁重建与状态冲突:父组件用
v-if="showWarningAlert"控制组件挂载,第一次触发错误后,Alert内部5秒后将show设为false,但父组件showWarningAlert仍为true。第二次触发相同错误时,父组件状态无变化,组件不会重新挂载;且两次错误消息相同,Alert内部的message监听不会触发,导致show保持false,无法显示。 - 直接修改Props:Alert组件的
showWarningAlert方法直接修改this.message(Props),违反Vue单向数据流原则,易引发状态不一致。 - finally块强制重置:无论请求成功或失败,finally块都会立即关闭提示,错误提示刚显示就被隐藏。
修复步骤
1. 重构WarningAlert组件
让父组件完全控制显示状态,组件仅负责接收消息和自动隐藏逻辑:
<!-- WarningAlert.vue --> <template> <div v-show="visible" class="overlay"> <div class="alert alert-warning" role="alert"> <div class="container"> <div class="alert-icon"> <i class="now-ui-icons ui-1_bell-53"></i> </div> <strong>{{ strongText }}</strong> {{ message }} <button v-if="dismissible" type="button" class="close" @click="handleClose" aria-label="Close"> <span aria-hidden="true"> <i class="now-ui-icons ui-1_simple-remove"></i> </span> </button> </div> </div> </div> </template> <script> export default { props: { message: String, strongText: String, dismissible: Boolean, visible: Boolean, }, data() { return { timer: null, }; }, watch: { visible(newVal) { if (newVal) { this.clearTimer(); this.timer = setTimeout(() => { this.$emit('close'); }, 5000); } else { this.clearTimer(); } }, }, methods: { handleClose() { this.$emit('close'); this.clearTimer(); }, clearTimer() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } }, }, beforeUnmount() { this.clearTimer(); }, }; </script> <style scoped> .overlay { position: fixed; top: 50px; left: 0; width: 100%; z-index: 1000; display: flex; justify-content: center; align-items: flex-start; padding: 20px; } .alert { width: 70%; } </style>
2. 修改父组件逻辑
- 用
v-show替代v-if,避免组件频繁销毁重建 - 移除finally块的强制重置,仅在提交成功后重置状态:
<!-- 父组件中Alert调用 --> <WarningAlert v-show="showWarningAlert" :message="warningMessage" :strongText="'Warning!'" :dismissible="true" @close="dismissWarningAlert" ></WarningAlert> <!-- submitForm方法修改 --> methods: { async submitForm() { this.dismissWarningAlert(); if (!this.form.firstName || !this.form.email || !this.form.phone) { this.warningMessage = '请填写所有必填字段(姓名、邮箱、电话)。'; this.showWarningAlert = true; return; } const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; if (!this.form.email.match(emailRegex)) { this.warningMessage = '请输入有效的邮箱地址。'; this.showWarningAlert = true; return; } const phoneRegex = /^(\+\d{1,3})?\d{1,14}$/; if (!this.form.phone.match(phoneRegex)) { this.warningMessage = '请输入有效的手机号码。'; this.showWarningAlert = true; return; } try { const response = await axios.post('http://localhost:3005/send-email', this.form); if (response.status === 200) { console.log('邮件发送成功'); this.form = { firstName: '', email: '', phone: '', message: '' }; this.dismissWarningAlert(); } } catch (error) { console.error('邮件发送失败:', error); this.warningMessage = '发送失败,请直接通过邮箱联系我们。'; this.showWarningAlert = true; } }, dismissWarningAlert() { this.showWarningAlert = false; this.warningMessage = ''; } },
内容的提问来源于stack exchange,提问作者HenrikB
相关产品推荐
相关产品推荐

