Vue 组件之间的通信还可以这样写

2,788 阅读2分钟

大家好,我是格子,今天我们用如下一个简单的表单填写,来演示另一种方式实现组件之间的通信。

先看效果:父组件对比

Snipaste_2024-12-30_21-52-46.png

Snipaste_2024-12-30_20-37-30.png

通常这种组件我们会为组件定义弹窗的状态(visible)、标题(title)、提交成功的回调方法(success)、表单回显数据(row)等。

用上面的思路代码如下:

子组件

<script setup lang="ts">
import {ref, watch} from "vue";

interface Person {
  name: string
  age: number
  gender: string
}

interface ModalParams {
  title: string
  row?: Person
  visible: boolean
  success: () => void
}

const props = defineProps<ModalParams>()
const modal = ref<Person>({} as Person)
const emit = defineEmits<(e: "update:visible", value: boolean) => void>()

// 弹窗状态相关
const visible = ref(false)
watch(() => props.visible, (value) => {
  // 处理表单回显逻辑
  modal.value = props.row ? props.row : {} as Person
  visible.value = value
})

/**
 * 模拟保存用户信息接口
 */
const saveInfo = (params: Person) => {
  return new Promise((resolve) => {
    console.log(params)
    setTimeout(() => {
      resolve(true)
      // 模拟需要保存 1.5 秒
    }, 1500)
  })
}

/**
 * 表单提交相关
 */
const isSubmit = ref(false)
const submitForm = async () => {
  isSubmit.value = true
  await saveInfo(modal.value)
  if (typeof props.success === 'function') {
    props.success()
  }
  isSubmit.value = false
  close()
}

/**
 * 关闭弹窗
 */
const close = () => {
  emit('update:visible', false)
}
</script>

<template>
  <el-dialog
      width="500px"
      :title="props.title"
      @close="close"
      v-model="props.visible">
    <el-form :model="modal">
      <el-form-item prop="name" label="姓名">
        <el-input v-model="modal.name" placeholder="请输入姓名"></el-input>
      </el-form-item>
      <el-form-item prop="age" label="年龄">
        <el-input-number
            style="width: 100%"
            v-model="modal.age"
            placeholder="请输入年龄"
            step-strictly
            :step="1"
            :precision="0"
            :min="0"
            :max="200"
            controls-position="right"
        ></el-input-number>
      </el-form-item>
      <el-form-item prop="gender" label="性别">
        <el-radio-group v-model="modal.gender">
          <el-radio value="1"></el-radio>
          <el-radio value="2"></el-radio>
        </el-radio-group>
      </el-form-item>
    </el-form>
    <div slot="footer" style="text-align: right;">
      <el-button @click="close">取 消</el-button>
      <el-button type="primary" :loading="isSubmit" @click="submitForm">确 定</el-button>
    </div>
  </el-dialog>
</template>

父组件

<script setup lang="ts">
import FormModal from "@/components/src/FormModal.vue";
import {ref} from "vue";
import {ElMessage} from 'element-plus'

const visible = ref(false)
const modalTitle = ref('新增')
const handleFillOutForm = () => {
  visible.value = true
}

const submitSuccess = () => {
  ElMessage.success('操作成功')
}
</script>

<template>
  <el-button @click="handleFillOutForm">填写表单</el-button>
  <FormModal
      v-model:visible="visible"
      :title="modalTitle"
      :success="submitSuccess"
  />
</template>

改写后的代码:

子组件

<script setup lang="ts">
import {ref} from "vue";

// 弹窗状态
const visible = ref(false)
// 弹窗标题
const modalTitle = ref('')
// 表单数据
const modal = ref<Person>({} as Person)

interface Person {
  name: string
  age: number
  gender: string
}

interface InitModalParams {
  title: string
  row?: Person
  success: () => void
}

/**
 * 初始化表单相关
 */
let callbackFunction: () => void
const initModal = ({row, title, success}: InitModalParams) => {
  // 处理表单回显逻辑
  modal.value = row ? row : {} as Person
  visible.value = true
  modalTitle.value = title
  if (typeof success === "function") {
    callbackFunction = success
  }
}

/**
 * 模拟保存用户信息接口
 */
const saveInfo = (params: Person) => {
  return new Promise((resolve) => {
    console.log(params)
    setTimeout(() => {
      resolve(true)
      // 模拟需要保存 1.5 秒
    }, 1500)
  })
}

/**
 * 提交表单相关
 */
const isSubmit = ref(false)
const submitForm = async () => {
  isSubmit.value = true
  await saveInfo(modal.value)
  callbackFunction()
  isSubmit.value = false
  visible.value = false
}

defineExpose({
  initModal
})
</script>

<template>
  <el-dialog
      width="500px"
      :title="modalTitle"
      v-model="visible">
    <el-form :model="modal">
      <el-form-item prop="name" label="姓名">
        <el-input v-model="modal.name" placeholder="请输入姓名"></el-input>
      </el-form-item>
      <el-form-item prop="age" label="年龄">
        <el-input-number
            style="width: 100%"
            v-model="modal.age"
            placeholder="请输入年龄"
            step-strictly
            :step="1"
            :precision="0"
            :min="0"
            :max="200"
            controls-position="right"
        ></el-input-number>
      </el-form-item>
      <el-form-item prop="gender" label="性别">
        <el-radio-group v-model="modal.gender">
          <el-radio value="1"></el-radio>
          <el-radio value="2"></el-radio>
        </el-radio-group>
      </el-form-item>
    </el-form>
    <div slot="footer" style="text-align: right;">
      <el-button @click="visible = false">取 消</el-button>
      <el-button type="primary" :loading="isSubmit" @click="submitForm">确 定</el-button>
    </div>
  </el-dialog>
</template>

父组件

<script setup lang="ts">
import FormModal from "@/components/FormModal.vue";
import {ref} from "vue";
import {ElMessage} from 'element-plus'

const formModalRef = ref()

const handleFillOutForm = () => {
  formModalRef.value.initModal({
    title: "新增",
    success: () => {
      ElMessage.success('操作成功')
    }
  });
}
</script>

<template>
  <el-button @click="handleFillOutForm">填写表单</el-button>
  <FormModal ref="formModalRef"/>
</template>

可以明显的看到父组件定义的变量减少了,不用单独定义方法处理表单填写成功的回调。