基礎知識
組件與組件之間有三大方面的知識點:
- 子組件通過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>