前端比较忙的时候, 我这个后端也得帮忙改一下前端bug, 但是当我看的下面的代码时, 我的内心是崩溃的.
这是公司内部用的管理后台, 一个vue组件1000多行, 看的我头大. 为啥这么大呢, 因为这个页面里面有很多对话框操作, 对话框里有交互逻辑, 甚至有网络请求. 因为有数据依赖前端把所有的弹框都写到一个页面里面了.并且打开,和关闭各个弹窗的逻辑是通过data对象里的变量控制的, 有多级弹框时非常的绕.
我常用的一个技巧就是, 把弹框封装成一个组件, 并且写一个函数去使用, 该函数接受参数, 并返回一个Promise, 等弹框操作完成时, 调用弹窗的地方能拿到处理结果, 至于调用方,完全不用管弹窗内部逻辑如何实现, 甚至不用控制弹窗是否显示. 方便后期维护. 下面提供一种简单的实现方式
DialogManager.js
:
import Vue from 'vue'
// 对话框管理器
const con = document.createElement('div')
document.body.appendChild(con)
// 复用组件实例
const instances = []
const comps = []
// 对话框组件混入
export const dialogMixin = {
data: {
visible: false,
promise: null,
param: {}
},
methods: {
open(param) { // 由弹窗管理器调用
this.visible = true
this.param = param
setTimeout(this.onOpen.bind(this, param))
return new Promise((resolve, reject) => {
this.promise = { resolve, reject }
})
},
resolve(data) { // 弹窗交互成功
this.visible = false
this.$options.promise.resolve(data)
},
reject(reason) { // 弹窗交互失败
this.visible = false
this.$options.promise.reject(reason)
},
getPromise() { // 给父组件调用, 用来获取和弹窗绑定的Promise
return this.$options.promise
},
onOpen(param) { // 弹窗组件通过这个方法接受父组件传过来的参数, 并进行初始化操作
throw new Error('请实现onOpen方法')
}
}
}
// 打开一个对话框组件并返回这个实例
export function openDialog(comp, param, recreate = true) {
var ins
// 构造Promise, 对话框操作完成销毁组件并移出dom
const promise = new Promise((resolve, reject) => setTimeout(() => Object.assign(promise, { resolve, reject })))
.finally(() => {
if (recreate) {
setTimeout(() => {
con.removeChild(ins.$el)
ins.$destroy()
}, 1e3)
}
})
if (!recreate && comps.indexOf(comp) > -1) {
const ins = instances[comps.indexOf(comp)]
ins.$options.promise = promise
ins.open(param)
return ins
}
// 强制混入
comp.mixins = comp.mixins || []
if (comp.mixins.indexOf(dialogMixin) < 0) {
comp.mixins.push(dialogMixin)
}
// 构造实例插入容器, 并把这个Promise通过options传达给组件实例
ins = new Vue({ promise, ...comp }).$mount()
con.appendChild(ins.$el)
ins.open(param)
if (!recreate) {
comps.push(comp)
instances.push(ins)
}
return ins
}
定义一个弹框specialAction.vue
, 此处省略
...
在父组件中使用:
import specialAction from './dialog/specialAction.vue'
import { openDialog } from '@/utils'
// 当某个按钮被点击时
async openSpecialActionDialog(action, row) {
await openDialog(specialAction, {
action,
bond: row
}).getPromise()
// 操作成功, 刷新债权列表
await this.refreshBondList()
}