vue3 父子组件间的数据传递
一、简介
二、实际代码
三、问题
一、简介
vue 父子组件的传递原则是单向的, 子组件是无法修改父组件的参数, 但是可以通过另一种渠道可以实现通信。
二、实际代码
1 父传子
1.1 props
父组件内容
<template> <div>这里是父组件</div> <input type="text" v-model="inputMsg"/> <div> inputMsg:{{inputMsg}} </div> <div> msg:{{msg}} </div> <div> <button @click="changeMsg">点击修改</button> </div> <div style="margin: 10px;">分割线-------------------------</div> <childView :toMsg="msg" :toFunc="func" ></childView> </template> <script setup> import { reactive, ref } from 'vue'; import childView from './edit.vue' const inputMsg = ref('这里是父组件的消息') const msg = ref('') const func = () =>{ changeMsg() } const changeMsg = () => { msg.value = inputMsg.value } </script>
这里父组件传入msg 和一个方法
子组件内容
<template> <div> <div>这里是子组件</div> <div>收到父组件消息toMsg:{{toMsg}}</div> </div> <div> <button @click="toFunc">点击执行收到的父组件方法</button> </div> <div> <button @click="changeMsg">点击修改toMsg</button> </div> </template> <script setup> // 需要定义defineProps const props = defineProps({ toMsg: { type: String, default: 100, }, toFunc: { type: Function, default: async () => {}, } }) const changeMsg = () => { props.toMsg = "子组件修改" } </script> <style> </style>
这样了就得到了这样一个界面
点击父组件的修改,就会修改msg 参数的值,并且会实时渲染到子组件
点击子组件里的 执行收到的父组件方法也可以达到同样的效果, 但是点击 子组件里的修改toMsg 就会报错
这就是vue的特性,父组件能修改值,但是子组件不能修改父组件的子,只能通过调用父组件的方法修改值。
顺带提一句,可以直接使用 inputMsg 传入子组件的toMsg,可以立即渲染。
<childView :toMsg="inputMsg" :toFunc="func" ></childView>
按照上面的逻辑,是不是可以回调方法传参呢,我将父组件稍微调整了下
<template> <div>这里是父组件</div> <input type="text" v-model="inputMsg"/> <div> inputMsg:{{inputMsg}} </div> <div> msg:{{msg}} </div> <div> <button @click="tapChangeMsg">点击修改</button> </div> <div style="margin: 10px;">分割线-------------------------</div> <childView :toMsg="msg" :toFunc="func" ></childView> </template> <script setup> import { reactive, ref } from 'vue'; import childView from './edit.vue' const inputMsg = ref('这里是父组件的消息') const msg = ref('')
// 增加参数 const func = (val) =>{ changeMsg(val) } const tapChangeMsg = () =>{ changeMsg() } const changeMsg = (val) => { if (!!val){ msg.value = val }else{ msg.value = inputMsg.value } } </script>
子组件传参
<template> <div> <div>这里是子组件</div> <div>收到父组件消息toMsg:{{toMsg}}</div> </div> <div> <button @click="toFunc('1')">点击执行收到的父组件方法</button> </div> <div> <button @click="changeMsg">点击修改toMsg</button> </div> </template>
实验结果,是可行的
2 子传父
除了上面的回调传参,还有一种其他方式,
emit: 组件封装了一系列 emit 事件,并返回数据、回调函数等,或单纯执行某个操作后,触发父组件的事件响应。 外部要用时,使用 @xxxx 来接收使用、响应。
2.1 实际内容
父组件
<template> <div>这里是父组件</div> <div> msg:{{msg}} </div> <div style="margin: 10px;">分割线-------------------------</div> <childView @backTo="func" ></childView> </template> <script setup> import { ref } from 'vue'; import childView from './edit.vue' const msg = ref('') const func = (val) =>{ msg.value = val } </script>
子组件
<template> <div> <button @click="changeMsg">点击修改父组件</button> </div> </template> <script setup> // 需要定义defineProps const emit = defineEmits(["backTo"]) const changeMsg = () => { emit("backTo", "子组件修改") } </script>
执行效果
这里就是使用 defineEmits 自定义了一个事件列表,使用@的方式。
3 父子组件双向绑定
这个的实现,需要使用到computed, 对于这个网上没有看到一个通俗详细的说法,只知道性能比调用方法好,会缓存。
3.1 实际代码
父组件
<template> <div>这里是父组件</div> <div> msg:{{msg}} </div> <button @click="change">父组件改</button> <div style="margin: 10px;">分割线-------------------------</div> <childView v-model:msg="msg" ></childView> </template> <script setup> import { reactive, ref } from 'vue'; import childView from './edit.vue' const msg = ref('') const change = () => { msg.value = "从父组件改" } </script>
子组件
<template> <div> {{value}} </div> <button @click="change">改变值</button> </template> <script setup> // 需要定义defineProps // 接受父组件传递过来的参数msg const props = defineProps(['msg']) // 定义子传父事件, update可默认执行父组件数据更新,父组件不需要调用此方法 const emit = defineEmits(['update:msg']) // 计算属性接受父组件传递过来的msg, 设置get、set方法实现数据双向绑定 const value = computed({ get: () => props.msg, set: (val) => emit('update:msg', val) }) const change = () => { value.value = "从子组件改" } </script> <style> </style>
这样相当于,父组件msg 变化会影响到 子组件value, 而子组件value变换会通过 emit 修改父组件的值。
注意:不管使用props 或者emits 在vue3 里面都需要使用 defineProps 和 defineEmits 声明。
4 注入传值。
vue3 还有一种传值方式 provide 和 inject
按照上面父子组件传值只能传一层,也就是当父子组件大于2以上层数时, 最上面一层往底层传参,按传统得一层一层传,使用这个,就可以直接传下去。
4.1 实际代码
父组件
<template> <div>这里是父组件</div> <div> msg:{{msg}} </div> <button @click="change">父组件改</button> <div style="margin: 10px;">分割线-------------------------</div> <childView ></childView> </template> <script setup> import { reactive, ref } from 'vue'; import childView from './edit.vue' const msg = ref('') const change = () => { msg.value = "从父组件改" } provide('proMsg',msg) </script>
子组件
<template> <div> {{ofMsg}} </div> <button @click="change">改变值</button> </template> <script setup> const change = () => { ofMsg.value = "从子组件改" } const ofMsg = inject('proMsg',ref('none')) </script> <style> </style>
实测,数据可以双向绑定,子组件修改,父组件可以改变,子组件修改,父组件会更改。
上面代码操作跟3.1的操作结果一致。
三、问题