基础知识
组件与组件之间有三大方面的知识点:
- 子组件通过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-formref="formRef":model="paramsForm"label-width="80px":rules="{processor: [{ required: true, message: '请选择处理人', trigger: 'blur' }]}"><el-form-item label="处理人" prop="processor"><el-selectv-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-inputv-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><!-- 子组件弹窗 --><ChildDialogv-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>