Vue2如何不使用TypeScript自定义类似this.$swal的全局$ask调用方法
实现方案(Vue2版本,Vue3适配方案附后)
核心思路是通过Vue插件+动态创建组件实例的方式实现,既可以加载.vue格式的模态框单文件组件,又能全局挂载可await的调用方法,完全符合你要的调用效果。
步骤1:编写模态框单文件组件
新建 src/components/AskModal.vue,内置模态框UI、表单渲染逻辑,对外暴露确认/取消事件:
<template> <!-- 可直接替换为项目在用UI框架的模态框,比如ElementUI的el-dialog --> <div class="ask-modal-mask" v-if="visible" @click.self="handleCancel"> <div class="ask-modal"> <div class="ask-modal-header">{{ modalConfig.title }}</div> <div class="ask-modal-body"> <div class="form-item" v-for="field in fieldConfig" :key="field.key"> <label>{{ field.title }}</label> <input v-model="formData[field.key]" :type="field.type" :placeholder="field.placeholder" > </div> </div> <div class="ask-modal-footer"> <button @click="handleCancel">{{ modalConfig.cancelButtonText }}</button> <button @click="handleConfirm" :class="`btn-${modalConfig.variant}`"> {{ modalConfig.confirmButtonText }} </button> </div> </div> </div> </template> <script> export default { data() { return { visible: false, fieldConfig: [], modalConfig: { variant: 'primary', icon: null, title: '提示', confirmButtonText: '确定', cancelButtonText: '取消' }, formData: {} } }, methods: { // 初始化传入配置 init(config) { this.fieldConfig = config.fields || [] this.modalConfig = Object.assign({}, this.modalConfig, config.modal || {}) // 初始化表单响应式字段 this.fieldConfig.forEach(field => { this.$set(this.formData, field.key, '') }) this.visible = true }, handleConfirm() { this.$emit('confirm', { data: this.formData }) this.visible = false }, handleCancel() { this.$emit('cancel') this.visible = false } } } </script> <style scoped> /* 可根据项目UI规范自定义样式 */ .ask-modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 9999; } .ask-modal { background: #fff; border-radius: 8px; min-width: 400px; padding: 20px; } .form-item { margin-bottom: 15px; } .form-item label { display: block; margin-bottom: 5px; } .form-item input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; } .ask-modal-footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; } .btn-primary { background: #409eff; color: #fff; border: none; padding: 8px 15px; border-radius: 4px; cursor: pointer; } </style>
步骤2:编写全局挂载插件
新建 src/plugins/ask.js,实现动态创建组件、返回Promise的核心逻辑:
import Vue from 'vue' import AskModal from '../components/AskModal.vue' // 创建模态框组件构造器 const AskModalConstructor = Vue.extend(AskModal) const askPlugin = { install(Vue) { // 挂载到Vue原型,所有组件可通过this.$ask直接调用 Vue.prototype.$ask = (config) => { return new Promise((resolve, reject) => { // 创建组件实例 const modalInstance = new AskModalConstructor() // 创建挂载节点并插入body const mountNode = document.createElement('div') document.body.appendChild(mountNode) modalInstance.$mount(mountNode) // 初始化模态框配置 modalInstance.init(config) // 监听确认事件,返回表单结果 modalInstance.$on('confirm', (result) => { resolve(result) // 动画结束后销毁组件、移除节点,避免内存泄漏 setTimeout(() => { modalInstance.$destroy() document.body.removeChild(mountNode) }, 300) }) // 监听取消事件 modalInstance.$on('cancel', () => { reject(new Error('用户取消输入')) setTimeout(() => { modalInstance.$destroy() document.body.removeChild(mountNode) }, 300) }) }) } } } export default askPlugin
步骤3:全局注册插件
在入口文件 main.js 中注册插件即可全局使用:
import Vue from 'vue' import App from './App.vue' import askPlugin from './plugins/ask' Vue.use(askPlugin) new Vue({ render: h => h(App) }).$mount('#app')
使用方式
和你期望的调用逻辑完全一致:
// 任意组件内调用 async function createTeam() { try { const modalResult = await this.$ask({ fields: [ { title: "Team name", placeholder: "Give your team a name!", key: "teamName", type: "text" }, { title: "Team description", placeholder: "What's your team about?", key: "teamDescription", type: "text" } ], modal: { variant: "primary", icon: null, title: "Make a new team", confirmButtonText: "Create team", cancelButtonText: "Cancel" } }) console.log("Team name: ", modalResult.data.teamName) console.log("Team description: ", modalResult.data.teamDescription) } catch (err) { // 可自定义用户点击取消后的处理逻辑 console.log('用户取消了输入') } }
Vue3适配调整
如果是Vue3项目,仅需修改插件部分的动态创建逻辑即可:
// src/plugins/ask.js(Vue3版本) import { createApp } from 'vue' import AskModal from '../components/AskModal.vue' const askPlugin = { install(app) { app.config.globalProperties.$ask = (config) => { return new Promise((resolve, reject) => { const mountNode = document.createElement('div') document.body.appendChild(mountNode) const appInstance = createApp(AskModal) const modalInstance = appInstance.mount(mountNode) modalInstance.init(config) modalInstance.$on('confirm', (result) => { resolve(result) setTimeout(() => { appInstance.unmount() document.body.removeChild(mountNode) }, 300) }) modalInstance.$on('cancel', () => { reject(new Error('用户取消输入')) setTimeout(() => { appInstance.unmount() document.body.removeChild(mountNode) }, 300) }) }) } } } export default askPlugin
内容的提问来源于stack exchange,提问作者Jonas Van der Reysen
相关产品推荐
相关产品推荐

