前端笔记(6) Vue3 dialog弹窗 父子组件之间传值及方法调用
·
dialog弹窗 父子组件之间传值及方法调用
一、前言
实际开发中,都是用单文件组件,那组件之间传值和调用方法是最常见的功能。
传参的方式有很多,比如props、emit、provide和inject。
在学习组件之间传值之前,需要了解下模板ref。
二、模板ref
<input ref="input">
ref 是一个特殊的 attribute,它允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。
1 访问模板ref
我们给一个组件标签赋值ref属性,我们还需要声明一个同名的 ref变量。
举例:在查询列表页面打开编辑页面。
- 列表页面是DictData.vue
- 编辑页面是DictDataEdit.vue
- <dict-data-edit> 的ref=“dictDataEditDialog”
- 在<script setup >中定义的组件ref引用也必须是dictDataEditDialog
</template>
<dict-data-edit
v-model:visible="viewState.editDialog.visible"
ref="dictDataEditDialog"
@close="closeEdit"
/>
</template>
<script setup lang="ts">
import {nextTick, reactive, ref} from "vue";
//这了的变量名称必须和<dict-data-edit>的ref值一样
const dictDataEditDialog = ref<any>();
const viewState = reactive({
editDialog: {visible: false} as Dialog
});
function handleEdit(id: string) {
viewState.editDialog.visible = true;
nextTick(() => {
//因为是ref创建的,所以得加.value
dictDataEditDialog.value.initEditData(id);
})
}
</script>
三、父给子传值
1 子组件使用prop声明接收的参数
子组件(编辑页面)需要接收一个参数:visible,这个visible是由父组件(列表页面)传给它的,用来控制子组件的是否显示。
子组件(编辑页面)声明了一个visible参数,控制dialog的是否显示。
<script lang="ts">
export default {
name: "DictTypeEdit"
}
</script>
<template>
<el-dialog
:model-value="visible"
width="500px"
/>
</template>
<script setup lang="ts">
//prop 可以使用 defineProps() 宏来定义
const props = defineProps(['visible'])
</script>
2 父组件使用v-model传递值
父组件(列表页面) 用v-model:visible传值
<template>
<dict-type-edit v-model:visible="viewState.editDialog.visible"
ref="dictTypeEditDialog"
/>
</template>
<script setup lang="ts">
import {reactive} from 'vue';
const dictDataEditDialog = ref<any>();
const viewState = reactive({
editDialog: {visible: false} as Dialog
});
</script>
四、父调子的方法
1 子组件定义方法,并暴露它
- 使用defineExpose()暴露方法,别的组件才能调用
<script lang="ts">
export default {
name: "DictTypeEdit"
}
</script>
<template>
<el-dialog
:model-value="visible"
width="500px"
>
省略Form表达的代码
</el-dialog>
</template>
<script setup lang="ts">
import {reactive} from "vue";
import {getDictTypeById} from '@/api/system/dictType'
import {DictType} from "@/types";
//注意:使用defineExpose()暴露方法,别的组件才能调用
defineExpose({initEditData});
let dataState = reactive({
dictType: {
state: '1'
} as DictType
});
//子组件定义了一个方法用来初始化数据
function initEditData(id: string) {
dataState.dictType.id = id;
if (id) {
getDictTypeById(id).then(({data}) => {
dataState.dictType = data
})
}
}
</script>
2 父组件调用子组件的方法
- 父组件在handleEdit方法里调用了子组件的方法
</template>
<dict-type-edit
v-model:visible="viewState.editDialog.visible"
ref="dictTypeEditDialog"
/>
</template>
<script setup lang="ts">
import {nextTick, reactive, ref} from "vue";
//这了的变量名称必须和<dict-type-edit>的ref值一样
const dictTypeEditDialog= ref<any>();
const viewState = reactive({
editDialog: {visible: false} as Dialog
});
function handleEdit(id: string) {
viewState.editDialog.visible = true;
nextTick(() => {
//因为是ref创建的,所以得加.value,调用子组件方法
dictDataEditDialog.value.initEditData(id);
})
}
</script>
五、子组件调用父组件方法
1 在父组件中定义方法,并在子组件标签上指定调用
- 在父组件中的<dict-type-edit>添加@close自定义方法,它执行父组件自己的closeEdit(val: any)方法
- closeEdit(val: any),这any就子组件传来的值
</template>
<dict-type-edit
v-model:visible="viewState.editDialog.visible"
ref="dictTypeEditDialog"
@close="closeEdit"
/>
</template>
<script setup lang="ts">
import {nextTick, reactive, ref} from "vue";
//这了的变量名称必须和<dict-type-edit>的ref值一样
const dictTypeEditDialog= ref<any>();
const viewState = reactive({
editDialog: {visible: false} as Dialog
});
function closeEdit(val: any) {
viewState.editDialog.visible = false;
}
</script>
2 子组件通过emit调用父组件的方法,并传值
- 子组件用defineEmits声明要调用父组件的方法名
- 用emit()实际调用父组件的方法
- 注意:defineEmits声明的是<dict-type-edit @close=“closeEdit”>的close,而不是closeEdit
- emit(‘close’, “子组件传给父组件的值”),close是方法名,第二个参数就是子组件传给父组件的值
<script lang="ts">
export default {
name: "DictTypeEdit"
}
</script>
<template>
<el-dialog
:model-value="visible"
width="500px"
close-on-click-modal=false
@close="cancel"
>
dialog关闭时,调用cancel方法
省略Form表达的代码
</el-dialog>
</template>
<script setup lang="ts">
import {onMounted, reactive, ref} from "vue";
import {getDictTypeById, saveDictType} from '@/api/system/dictType'
import {DictType} from "@/types";
import {ElForm, ElMessage} from "element-plus";
const props = defineProps(['visible'])
//用defineEmits声明要调用父组件的方法
//注意:这个是父组件<dict-type-edit>标签上的@close
//而不是父组件自己定义的方法closeEdit()
const emit = defineEmits(['close'])
let dataState = reactive({
dictType: {
state: '1'
} as DictType
});
function cancel() {
//执行调用<dict-type-edit @close="closeEdit">的close
emit('close', "子组件传给父组件的值");
}
</script>
六、子给父传值,配合v-model:使用
这里用场景的分页插件做介绍,分页插件可以修改页号,和每页的大小
1 父组件中使用子组件<pagination>
<template>
<pagination
v-if="dataState.total > 0"
:total="dataState.total"
v-model:page="dataState.queryParams.pageSearch.pageNumber"
v-model:limit="dataState.queryParams.pageSearch.pageSize"
@pagination="handleQuery"
></pagination>
</template>
<script setup lang="ts">
import {reactive, ref} from 'vue';
const dataState = reactive({
total: 10,
//查询列表页面的数据
queryParams: {
pageSearch: {
page: true,
pageNumber: 1,
pageSize: 10
}
} as DictTypePageQueryParam
});
const viewState = reactive({
loading: false,
editDialog: {visible: false} as Dialog
});
function handleQuery() {
//省略
}
</script>
2 子组件用emit(‘update:limit’, value),触发更新值
子组件中,有如下代码
const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
当limit的值有变化时,emit(‘update:limit’, value)就会更新父组件上limit的值
//每页大小
const pageSize = computed<number | undefined>({
get: () => props.limit,
set: value => {
emit('update:limit', value)
}
})
<template>
<div :class="{ hidden: hidden }" class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</div>
</template>
<script setup lang="ts">
import {computed, PropType} from "vue";
const props = defineProps({
total: {
required: true,
type: Number as PropType<number>,
default: 0
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array as PropType<number[]>,
default() {
return [10, 20, 30, 50]
}
},
//分页组件布局
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
hidden: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
//当前页号
const currentPage = computed<number | undefined>({
get: () => props.page,
set: value => {
emit('update:page', value)
}
});
//每页大小
const pageSize = computed<number | undefined>({
get: () => props.limit,
set: value => {
emit('update:limit', value)
}
})
function handleSizeChange(val: number) {
emit('pagination', {page: currentPage, limit: val})
if (props.autoScroll) {
scrollTo(0, 800)
}
}
</script>
七、provide和inject
当组件的嵌套层级比较深的时候,用props就不合适了。
对于这种情况,我们可以使用一对 provide 和 inject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。
- 父级组件使用provide向下进行传递数据;
- 子级组件使用inject来获取上级组件传递过来的数据;
- provide可以提供值和方法。
父组件provide
<script setup>
import { provide } from 'vue'
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>
子组件inject
<script setup>
import { inject } from 'vue'
const message = inject('message', '这是默认值')
</script>
更多推荐
已为社区贡献12条内容
所有评论(0)