基础知识
组件与组件之间有三大方面的知识点:
- 子组件通过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>