vue中如何封装弹窗组件才能使项目更好维护

97 阅读1分钟

业务场景:

弹窗一般用于数据编辑、数据选择和动作确认,归纳而言就是作为某个流程的中间环节

已知实现方案和优劣:

1.(暂定)把弹窗及内容封装为一个组件,该组件暴露一个返回 promise 的 open 方法来打开弹窗。代码如下:

  • 父组件中使用封装好的弹窗组件 CustomersDetail.vue
<template>
  <div>
    <el-button @click="addCustomers">新增客户</el-button>
    <CustomersDetail ref="customersDetail"></CustomersDetail>
  </div>
</template>
<script>
import { defineComponent, ref } from '@vue/composition-api'
import CustomersDetail from './CustomersDetail.vue'

export default defineComponent({
  components: { CustomersDetail },
  setup() {
    const customersDetail = ref(null)
    const addCustomers = async () => {
      const customersData = await customersDetail.value.open()
      // ...doSome
    }
    return {
      addCustomers,
      customersDetail
    }
  }
})
</script>
  • 弹窗组件 CustomersDetail.vue 中使用 useDialog
<!-- CustomersDetail.vue -->
<template>
  <el-dialog title="新增客户" :visible="visible" v-loading="loading" append-to-body id="app" 
  @close="cancel" center>
    <el-form :model="formData">
      <el-col span="8">
        <el-form-item label="客户姓名:">
          <el-input v-model="formData.customerName"></el-input>
        </el-form-item>
      </el-col>
      <el-col span="8">
        <el-form-item label="客户电话:">
          <el-input v-model="formData.customerPhone"></el-input>
        </el-form-item>
      </el-col>
      <el-col span="8">
        <el-form-item label="客户邮箱:">
          <el-input v-model="formData.customerEmail"></el-input>
        </el-form-item>
      </el-col>
    </el-form>
    <span slot="footer">
      <el-button type="primary" @click="confirm(formData)">确认</el-button>
      <el-button type="primary" @click="cancel">取消</el-button>
    </span>
  </el-dialog>
</template>
<script>
import { defineComponent, reactive } from '@vue/composition-api'
import { useDialog } from './useDialog'
function initFormData() {
  return {
    customerName: '',
    customerPhone: '',
    customerEmail: ''
  }
}

export default defineComponent({
  setup() {
    const { visible, openFactory, loading, confirm, cancel } = useDialog()
    const formData = reactive(initFormData())
    const open = openFactory((params) => {
      Object.assign(formData, initFormData(), params)
    })
    return {
      confirm,
      visible,
      loading,
      cancel,
      formData,
      open
    }
  }
})
</script>
 
  • 封装弹窗公共逻辑为 useDialog
export const useDialog = () => {
  const visible = ref(false)
  const openFactory = (init) => {
    return (params) => {
      return new Promise(async (resolve, reject) => {
        visible.value = true
        loading.value = true
        await init(params)
        loading.value = false
        resolveFun = resolve
      })
    }
  }
  let resolveFun
  const loading = ref(false)
  const confirm = (dialogData) => {
    visible.value = false
    resolveFun(dialogData)
  }
  const cancel = () => {
    visible.value = false
    resolveFun(null)
  }
  return {
    visible,
    openFactory,
    loading,
    confirm,
    cancel
  }
}

2.采用传入 visible 打开弹窗,监听 confirm 和 cancel 事件获取用户操作的结果

  • 缺点:
    • 在父组件需要定义并操作 visible 状态
    • 打开弹窗的动作和后续的行为位于两个独立函数中,不利于业务理解