当前位置: 首页 > news >正文

vue3父组件和子组件之间传递数据

vue3中父子组件之间如何传递数据这个话题已经被讨论了无数遍,最近在写一个涉及到父子组件间传递数据的程序,理解又深刻了一点,记录下来以备将来查询。

组件的内容

比如说一个外部库mylib.ts是这样写的

export const enum EDIT_MODE {EDIT = 1,VIEW = 2,
}export interface DataType {name: string,comment: string,
}

一个基于element-plus的子组件这样写

<template><el-dialog v-model="visibleRef"><el-input v-model="dataRef.name" v-bind:disabled="editableRef" /><el-input v-model="dataRef.comment" v-bind:disabled="uneditableRef" type="textarea" /><template v-slot:footer><el-button type="primary" v-bind:disabled="uneditableRef" @click="Sumit()"> 确认 </el-button><el-button @click="HandleCancle">取消</el-button></template></el-dialog>
<template>
<script setup lang="ts">
import { EDIT_MODE, DataType } from "./mylib";
import { Ref, ref, Reactive, reactive, PropType, watch } from "vue";const props = defineProps({editModeRef: {type: Number as PropType<EDIT_MODE>,required: true,},data: {type: Object as PropType<DataType>,required: true,},
});const emit = defineEmits<{(event: "Sumit", val: DataType): void;(event: "Cancle"): void;
}>();let visibleRef: Ref<boolean> = ref<boolean>(false);
let uneditableRef: Ref<boolean> = ref<boolean>(false);let dataRef: Reactive<DataType> = reactive<DataType>({name: props.data.name,comment: props.data.comment,
});function Sumit(): void {emit("Sumit", dataRef);
}function HandleCancle(): void {// 虽然visibleRef是v-model绑定的,但是visibleRef来自父组件,无法在子组件内修改// 所以只能发送事件,然后在父组件修改emit("Cancle");
}// props.data本来就是一个object,所以前面defineProps的时候,直接用type: Object as PropType<DataType>就可以
// 然后watch的时候直接作为监控目标处理即可
watch(props.data,(newVal: DataType, oldVal: DataType) => {dataRef.name = newVal.name;dataRef.comment = newVal.comment;},{ deep: true }
);// editModeRef是enum,内容是整型数字,所以前面defineProps的时候,要用type: Number as PropType<EDIT_MODE>
// watch的时候,第一个参数是监控目标,这里必须要用匿名函数返回才能正确识别
watch(() => props.editModeRef,(newVal: number, oldVal: number) => {uneditableRef.value = newVal == EDIT_MODE.VIEW;}
);
</script>
<style lang="less" scoped></style>

父组件的内容是这样的

<template>这里还有一些父组件自己的内容<el-button type="primary" v-on:click="ShowDialog"> 显示对话框 </el-button><el-button type="primary" v-on:click="SwitchDialogEdit"> 变更对话框编辑许可 </el-button><CustomDialogv-model="dialogVisibleRef":edit-mode-ref="workFlowTypeStore.mode":data="dataRef"@SumitWorkFlow="SubmitData"@Cancle="DialogCancle"></CustomDialog>
</template>
<script lang="ts" setup>
import { EDIT_MODE, DataType } from "./mylib";
import { Ref, ref, onMounted, Reactive, reactive } from "vue";let dialogVisibleRef: Ref<boolean> = ref<boolean>(false);
let editModeRef: Ref<EDIT_MODE> =ref<EDIT_MODE>(EDIT_MODE.VIEW);// 初始化默认数据
let dataRef: Reactive<DataType> = reactive<DataType>({name: "",comment: "",
});// 子组件emit Submit事件,父组件接收随事件的数据
function SubmitData(inputData:DataType):void{dataRef.name=inputData.name;dataRef.comment=inputData.comment;console.log(dataRef.name);console.log(dataRef.comment);
}function ShowDialog():void{dialogVisibleRef.value = true;
}// 子组件emit Cancle事件,没有携带附加数据
function DialogCancle():void{dialogVisibleRef.value = false;
}function SwitchDialogEdit():void{if (editModeRef.value==EDIT_MODE.VIEW){editModeRef.value=EDIT_MODE.EDIT;}else{editModeRef.value=EDIT_MODE.VIEW;}
}
</script>

数据传递的单向性

vue3推荐的父组件向子组件传递数据的方法是props,并且强调数据传递是单向的,也就是说子组件无法更改父组件的内容。

但其实vue3父组件向子组件传递数据还有一个方法,就是v-model,这个方法和props类似,数据传递是单向的,子组件无法更改父组件的内容。

这就有了一个容易让人迷惑的点,v-model不是双向绑定吗?在同一个组件内v-model的确是双向绑定的,但是如果是父组件向子组件传递,就是单向的。

注意案例代码中子组件内部的visibleRef,它是v-model双向绑定的,但是在子组件内是无法改变这个变量的。如果说在子组件内的取消按钮是这样的

<el-button @click="visibleRef=false">取消</el-button>

你会发现,无论你怎么点击,这个对话框都不会隐藏。这就是因为子组件无法改变父组件通过v-model传递来的visibleRef变量。

如果想要修改这个变量来实现隐藏对话框的功能,只能把取消按钮绑定HandleCancle函数,然后在子组件内通过HandleCancle函数emit一个事件


function HandleCancle(): void {emit("Cancle");
}

在父组件内接收这个事件,并且修改父组件内控制对话框显示的dialogVisibleRef变量才能实现点击取消按钮隐藏对话框的功能。

传递复杂格式的数据

如果只是传递一些string、number之类简单的数据,直接在子组件里


const props = defineProps({simple: {type: Number,required: true,},another: {type: String,required: true,},
});

甚至还可以增加一些validator之类。

但是如果要传递enum或者更加复杂的数据,就没这简单了。

enum数据的传递

首先要强调一点:enum不是JavaScript自带的数据类型,而是TypeScript引入的类型。而TypeScript实际上会把enum转化为更基础的类型,比如number或者string,这取决于enum类型定义的时候所赋的值,比如说,前面的代码里赋值为1和2,那就会转化为number

根据这个赋值的类型,在子组件里要这样

import { EDIT_MODE } from "./mylib";
import { PropType } from "vue";
const props = defineProps({editModeRef: {type: Number as PropType<EDIT_MODE>,required: true,},
});

比如说,例子中的代码EDIT_MODE这个枚举类型其实是数字,那就把editModeRef的类型设定为Number as PropType<EDIT_MODE>,PropType是vue中的定义。

对象数据的传递

如果是传递更复杂的object类型,那么要这么处理

import { DataType } from "./mylib";
import { PropType } from "vue";const props = defineProps({data: {type: Object as PropType<DataType>,required: true,},
});

因为DataType在TypeScript中最终会被编译为object,所以要用Object as PropType<DataType>来定义传递数据的类型

传递数据的监控

采用了特殊的数据传递方式,其配套的监控方式也要做出调整

enum变量的监控

因为enum数据的类型从enum变为了number,所以watch的时候必须要做点处理,watch的第一个参数——监控目标——必须要用匿名函数返回对应的变量。

import { EDIT_MODE} from "./mylib";
import { PropType, watch } from "vue";const props = defineProps({editModeRef: {type: Number as PropType<EDIT_MODE>,required: true,},
});// editModeRef是enum,内容是整型数字,所以前面defineProps的时候,要用type: Number as PropType<EDIT_MODE>
// watch的时候,第一个参数是监控目标,这里必须要用匿名函数返回才能正确识别
watch(() => props.editModeRef,(newVal: number, oldVal: number) => {uneditableRef.value = newVal == EDIT_MODE.VIEW;}
);

对象变量的监控

因为DataType本质上是object的子类型,所以在defineProps的时候,其实最终TypeScript代码也会被编译为object,也就是说其实没变。watch监控的时候就可以直接写,不需要使用匿名函数返回。

import { DataType } from "./mylib";
import { PropType, watch } from "vue";const props = defineProps({data: {type: Object as PropType<DataType>,required: true,},
});// props.data本来就是一个object,所以前面defineProps的时候,直接用type: Object as PropType<DataType>就可以
// 然后watch的时候直接作为监控目标处理即可
watch(props.data,(newVal: DataType, oldVal: DataType) => {dataRef.name = newVal.name;dataRef.comment = newVal.comment;},{ deep: true }
);

前面提到了v-model传递的变量能不能这样处理:子组件中创建一个内部变量,然后监控传递进来的变量,然后在对话框绑定的是内部变量,“取消”按钮通过调整内部变量来处理对话框的可见与否。

这是不可以的。

el-dialog是否可见是用v-model绑定的,而在父组件嵌入子组件的时候,就是通过v-model绑定的,所以无法通过子组件的内部变量来操纵其已经绑定的来自于父组件的v-model。

不论是v-model还是props从父组件传递到子组件,都是不能在子组件更改的。子组件若是想要更改,就只能通过emit事件来让父组件来更改。

http://www.dtcms.com/a/437952.html

相关文章:

  • Coze源码分析-资源库-编辑工作流-前端源码-核心流程/API/总结
  • Netty服务器监听读写超时
  • PHP 中的正则表达式
  • Linux的Socket编程之UDP
  • 环境没有tomcat怎么演示自己做的网站动漫设计专业就业方向
  • 180课时吃透Go语言游戏后端开发7:Go语言中的函数
  • Python核心架构深度解析:从解释器原理到GIL机制全面剖析
  • 数据结构_哈夫曼编码(Huffman)完整指南:从原理到实现,附考研真题详解
  • 怎样做网站吸引客户网站开发专业就业前系军
  • 四川建站模板网站公司有哪些做任务网站
  • 藏语自然语言处理入门 - 5 文本归类
  • Stanford CS336 assignment1 | Transformer Language Model Architecture
  • 告别人工出题!PromptCoT 2.0 让大模型自己造训练难题,7B 模型仅用合成数据碾压人工数据集效果!
  • Prompt Programming - 用文字重构AI智能体系
  • 基于提示学习的多模态情感分析系统:从MULT到PromptModel的华丽升级
  • Node.js 图形渲染库对比:node-canvas 与 @napi-rs/canvas
  • 【LangChain】P10 LangChain 提示词模板深度解析(一):Prompt Template
  • C# TCP 服务端开发笔记(TcpListener/TcpClient)
  • 180课时吃透Go语言游戏后端开发6:Go语言的循环语句
  • wordpress+vps建站关键词语有哪些
  • 网站建设基本标准野花高清中文免费观看视频
  • hadoop-hdfs
  • VB6.0找不到该引用word,excel“Microsoft Excel 16.0 Object Library”解决方法
  • 读者-写者问题实现真正的写优先
  • 北京人力资源网站县区网站集约化建设
  • 从零开始,用WPS和DeepSeek打造数字人科普视频
  • netgear r6220 路由器,刷openwrt后,系统备份还原
  • 特价流量网站什么情况自己建设网站
  • 昂瑞微IPO前瞻:技术破局高端射频模组,国产替代第二波浪潮下的硬科技突围
  • 开源 全平台 哔哩哔哩缓存视频合并 Github地址:https://github.com/molihuan/hlbmerge_flutter