揭秘:父子组件之间的传递

发布于:2025-04-02 ⋅ 阅读:(16) ⋅ 点赞:(0)

基础知识

组件与组件之间有三大方面的知识点:

  • 子组件通过props = defineProps({})接收父组件传递到参数和方法;
  • 子组件可以通过定义 emit 事件,向父组件发送事件;
  • 父组件调用子组件通过defineExpose 导出的方法

一、props==>接收父组件的参数和方法

子组件通过props = defineProps({})可以接收父组件传来的变量参数和方法;

示例如下:

const props = defineProps({
  page: number, //v-model:page vue3中的v-model新特性,支持多个v-model的数据绑定
  total:number, //普通传递参数
  queryParams: {   // 初始表单数据
    type: Object,
    default: () => ({
      processor: '',
      remark: ''
    })
  },
  getList: {  //父组件方法
    type: Function,
    default: () => {
    }
  });

父组件传递的方法和参数 ,具体示例如下:假设子组件为TabComponent

<TabComponent v-model:page="pageValue" ref="LegShippingPlanTabComponent" :total="total" :queryParams="queryParams" :getList="getList" />

总结:父组件通过“ : ”向子组件传递参数和方法,子组件通过props = defineProps({}),后期子组件直接通过 prop.page 使用参数

示例如下:

props.queryParams.pageNum = 1;
props.queryParams.planStatus = pane.paneName;
props.getList(); //使用父组件方法

二、emit==>向父组件发出 emit 事件(参数和方法事件)

子组件定义 emit 事件,在需要的地方调用 emit 事件向父组件发出 emit 事件;

事件可以分为两种一个是参数,一个是方法

案例一:v-model:propName 类型,实现数据的双向绑定

父组件

<TabComponent v-model:page="pageValue" ref="LegShippingPlanTabComponent" :queryParams="queryParams" :getList="getList" />

拓展知识点

这是 vue3 中的 v-model 中的新特性:

  • Vue 3 允许自定义组件有多个 v-model 绑定,语法格式为 v-model:propName
  • 默认 v-model (绑定到 modelValue):

子组件

先通过props = defineProps({})接收到数据,然后定义 emit()更新事件

const props = defineProps({
  total: propTypes.number,
  page: propTypes.number.def(1),
  limit: propTypes.number.def(20),
  pageSizes: {
    type: Array as PropType<number[]>,
    default: () => [10, 20, 30, 50]
  },
})
const emit = defineEmits(['update:page']);
//向父组件发出emit事件
const save=()=>{
  emit('update:page', val); //update:page发出这个事件,val是page的值;
}

总结:这里就实现了 父子组件的双向绑定;

案例二:子组件通过 emit 发送方法事件

子组件:

首先定义 emit 事件,然后调用 emit 发送事件

const props = defineProps({
  total: propTypes.number,
  page: propTypes.number.def(1),
  limit: propTypes.number.def(20),
  pageSizes: { //小驼峰
    type: Array as PropType<number[]>,
    default: () => [10, 20, 30, 50]
  }
});
const emit = defineEmits(['pagination']);
//发送emit事件,不传递参数
const handleAdd=()=>{
  emit('pagination')
}
//发送emit事件 可以传递参数
const handleSubmit=()=>{
  emit('pagination', { page: val, limit: pageSize.value });
}

父组件:

通过@ pagination 接收,父组件中的这个 pagination 的名字需要与子组件中的 emit 事件名称一样及const emit = defineEmits(['pagination']);

<pagination
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
:page-sizes="[10, 20, 50, 100, 500, 1000, 2000]"
  />

总结:分为三步,一是子组件中定义emit事件;二是发出emit事件;三父组件通过@pagination响应


三、defineExpose

父组件调用子组件通过defineExpose 导出的方法)

子组件

通过defineExpose 导出(暴露)子组件的方法

<script setup name="LegShippingPlanTabComponent" lang="ts">
const getTabStatusCount = async () => {
  const res = await getTabCount();
  activeTabCount.value = res.data;
}

defineExpose({
  getTabStatusCount,
})
</script>

父组件:

先定义一个 ref,然后通过.vale.getTabStatusCount()直接调用子组件的方法;

<TabComponent ref="LegShippingPlanTabComponent" :queryParams="queryParams" :getList="getList" />
<script setup name="LegShippingPlan" lang="ts">
  const LegShippingPlanTabComponent = ref()
/** 查询头程发货计划列表 */
const getList = async () => {
  await LegShippingPlanTabComponent.value.getTabStatusCount();
}
  
</script>

总结:如果父组件需要用子组件defineExpose的方法,一定要在使用这个子组件中加一个 ref,通过它去调用子组件的方法;


四、实战案例

子组件

<script setup>
import { ref, watch } from 'vue';

const props = defineProps({
  modelValue: Boolean, // 控制弹窗显示
  initialFormData: {   // 初始表单数据
    type: Object,
    default: () => ({
      processor: '',
      remark: ''
    })
  }
});

const emit = defineEmits(['update:modelValue', 'submit']);

// 表单数据
const paramsForm = ref({ ...props.initialFormData });

// 表单引用(用于验证)
const formRef = ref(null);

// 监听弹窗打开时重置表单
watch(() => props.modelValue, (visible) => {
  if (visible) {
    paramsForm.value = { ...props.initialFormData };
  }
});

// 提交表单
const handleSubmit = async () => {
  try {
    // 表单验证
    await formRef.value.validate();
    
    // 提交数据并关闭弹窗
    emit('submit', { ...paramsForm.value });
    emit('update:modelValue', false);
    
  } catch (error) {
    console.error('表单验证失败:', error);
  }
};

// 关闭弹窗
const closeDialog = () => {
  emit('update:modelValue', false);
};
</script>

<template>
  <el-dialog
    :model-value="modelValue"
    @update:model-value="$emit('update:modelValue', $event)"
    title="转交处理人"
    width="500px"
  >
    <el-form
      ref="formRef"
      :model="paramsForm"
      label-width="80px"
      :rules="{
        processor: [{ required: true, message: '请选择处理人', trigger: 'blur' }]
      }"
    >
      <el-form-item label="处理人" prop="processor">
        <el-select
          v-model="paramsForm.processor"
          placeholder="请选择处理人"
          style="width: 100%"
        >
          <el-option label="张三" value="zhangsan" />
          <el-option label="李四" value="lisi" />
          <el-option label="王五" value="wangwu" />
        </el-select>
      </el-form-item>
      <el-form-item label="备注">
        <el-input
          v-model="paramsForm.remark"
          type="textarea"
          placeholder="请输入备注信息"
        />
      </el-form-item>
    </el-form>

    <template #footer>
      <el-button @click="closeDialog">取消</el-button>
      <el-button type="primary" @click="handleSubmit">确定</el-button>
    </template>
  </el-dialog>
</template>

父组件

<script setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
import ChildDialog from './ChildDialog.vue';
import { updateProcessor } from '@/api/your-api'; // 替换为你的API

// 控制弹窗显示
const dialogVisible = ref(false);

// 父组件表单数据
const formData = ref({
  id: '',       // 假设有ID字段
  title: '',    // 其他表单字段
  processor: '', // 处理人
  remark: ''    // 备注
});

// 打开弹窗
const openTransferDialog = () => {
  dialogVisible.value = true;
};

// 接收子组件数据并发请求
const handleSubmit = async (transferData) => {
  try {
    // 1. 更新本地表单数据
    formData.value.processor = transferData.processor;
    formData.value.remark = transferData.remark;
    
    // 2. 调用API更新处理人
    const res = await updateProcessor({
      id: formData.value.id,
      processor: transferData.processor,
      remark: transferData.remark
    });
    
    // 3. 提示成功
    ElMessage.success('处理人更新成功');
    
    // 4. 可以在这里刷新数据或执行其他操作
    // fetchData();
    
  } catch (error) {
    ElMessage.error('更新处理人失败: ' + error.message);
    console.error('API请求错误:', error);
  }
};
</script>

<template>
  <div class="parent-container">
    <!-- 主表单 -->
    <el-form :model="formData" label-width="100px">
      <el-form-item label="标题">
        <el-input v-model="formData.title" disabled />
      </el-form-item>
      <el-form-item label="当前处理人">
        <el-input v-model="formData.processor" disabled />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="openTransferDialog">
          转交处理人
        </el-button>
      </el-form-item>
    </el-form>

    <!-- 子组件弹窗 -->
    <ChildDialog
      v-model="dialogVisible"
      @submit="handleSubmit"
      :initial-form-data="{
        processor: formData.processor,
        remark: formData.remark
      }"
    />
  </div>
</template>

<style scoped>
.parent-container {
  padding: 20px;
  max-width: 800px;
  margin: 0 auto;
}
</style>


网站公告

今日签到

点亮在社区的每一天
去签到