vue父子组件通讯的所有方法小结
作者:今天一定晴q
父子组件通讯
父组件展示输入框用于新增数据,子组件展示数组信息
场景一:直接传递一整个数组
父组件在子组件标签中使用v-bind
绑定一个属性名为list
,属性值为要传递的数组list
子组件调用函数defineProps
,接收父组件传过来的数据。defineProps
函数是vue默认帮我们引入好了的,它默认接收一个对象作为参数,该对象包含一个字段,字段名为父组件绑定的属性名list
,字段值又为一个对象,对象中包含子组件期望接收到的数据类型type
和默认值default
,此处期望接收到一个数组,默认值为一个空数组。list
可以直接拿到template模块中使用
父组件代码:
<template> <div class="inputGroup"> <input type="text" v-model="value"> <button @click="add">添加</button> </div> <Child :list="list"> </Child> // 父组件将值v-bind绑定传给子组件 </template> <script setup> import Child from '@/components/child.vue' // 引入子组件 import { ref } from 'vue' const value = ref('') const list = ref(['html', 'css', 'js']) const add = () => { list.value.push(value.value) value.value = '' } </script>
子组件代码:
<template> <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div> </template> <script setup> defineProps({ // 子组件使用defineProps接收 list: { type: Array, required: true, default: () => [] } }) </script>
这个通讯过程本身就是一个响应式的过程,所以父组件向子组件传递过去的属性值 list 发生改变后,子组件会重新接收一遍最新的值,由于 list 被定义成了响应式,浏览器最终就会将新添加的值成功渲染出来
场景二:只传递新增加的那个值
父组件在子组件标签中使用v-bind
绑定一个属性名为msg
,属性值为要传递的新增数据toChild
子组件定义一个变量props
接收defineProps
函数执行的结果,再将接收到的新增数据 props.msg
添加进子组件已经定义好的list
数组中
父组件代码:
<template> <div class="inputGroup"> <input type="text" v-model="value"> <button @click="add">添加</button> </div> <Child :msg="toChild"> </Child> </template> <script setup> import Child from '@/components/child.vue' // 引入子组件 import { ref } from 'vue' const value = ref('') const toChild = ref('') const add = () => { toChild.value = value.value } </script>
子组件代码:
<template> <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div> </template> <script setup> import { ref, watch } from 'vue' const list = ref(['html', 'css', 'js']) const props = defineProps({ msg: '' // 直接简写,不再把msg对应的值写成一个对象 }) watch( // 监视props.msg的值的变化,一旦变化执行回调函数 () => props.msg, (newVal, oldVal) => { list.value.push(newVal) } ) </script>
defineProps
函数中的字段可以直接被拿到template中使用,但如果要在js脚本中使用,需要一个变量来接收这个函数的执行结果
注意:这里不直接
list.value.push(props.msg)
,而是需要watch
对props.msg
的值进行监听是因为:list的更新需要list.value.push()
这句js代码反复去执行,而js代码在浏览器第一遍渲染页面完成后,不会再执行第二遍,所以需要watch函数在监视到值变化后,主动去执行list的更新
子父组件通讯一
父组件展示数组信息,子组件展示输入框用于新增数据
借助发布订阅机制,子组件调用defineEmits
函数接受一个数组作为参数,数组 [ 'new' ]
表示组件可以触发一个名为 'new'
的自定义事件,返回给emits
对象,可以用它来触发 new
事件。点击按钮后,调用emits
函数发布事件,传递的参数分别为要传输给父组件的事件名new
和事件值value.value
父组件订阅该事件,通过事件参数获取子组件提供的值
父组件代码:
<template> <!-- 订阅new事件--> <Child @new="handle"></Child> <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div> </template> <script setup> import Child from '@/components/child2.vue' import { ref } from 'vue' const list = ref(['html', 'css', 'js']) const handle = (event) => { // event事件参数,其实就是子组件发布事件时传输过来的值 list.value.push(event) } </script>
子组件代码:
<template> <div class="inputGroup"> <input type="text" v-model="value"> <button @click="add">添加</button> </div> </template> <script setup> import { ref } from 'vue' const value = ref('') const emits = defineEmits(['new']) // 创建一个new事件 const add = () => { emits('new', value.value) // 发布事件 } </script>
子父组件通讯二
仍然是父组件展示数组信息,子组件展示输入框用于新增数据
能用但不建议版
如果将list
数组看做成篮子,新增数据看做成苹果,那么子父组件通讯一就是儿子把苹果丢给父亲,父亲再将苹果装入篮中;子父组件通讯二就是父亲把篮子共享给了儿子,儿子将苹果装入篮中
父组件定义了list
数组,通过v-model:list
指令将父组件的 list
属性与子组件中的 list
prop 进行了双向绑定,意味着当在子组件内部修改 list 时,这些更改也会反映回父组件的 list 属性中
子组件接收list的方式就是使用defineProps
接收
父组件代码:
<template> <Child v-model:list="list"></Child> <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div> </template> <script setup> import Child from '@/components/child3.vue' import { ref } from 'vue' const list = ref(['html', 'css', 'js']) </script>
子组件代码:
<template> <div class="inputGroup"> <input type="text" v-model="value"> <button @click="add">添加</button> </div> </template> <script setup> import { ref, defineProps } from 'vue' const value = ref('') const props = defineProps({ list: { type: Array, default: () => [] } }) const add = () => { props.list.push(value.value) } </script>
但是vue官方不建议我们让子组件直接操作父组件给过来的数据,因为这样会导致数据流很混乱。正常来讲我自己的数组想要被修改,就应该由我自己来改,而不是交到别人手上去改
优化版
所以子组件的js代码应当优化成下面的样子
使用 defineEmits
函数来声明组件可以触发的事件update:list
,在 Vue 中,以 update:
开头的事件通常用于通知父组件 子组件内部数据的变更。arr
接收list
prop 的引用,再把更新过后的arr
抛出出去,v-model:list
就会自动get到最新的值
<script setup> import { ref, defineProps } from 'vue' const value = ref('') const props = defineProps({ list: { type: Array, default: () => [] } }) const emits = defineEmits(['update:list']) const add = () => { const arr = props.list arr.push(value.value) emits('update:list', arr) } </script>
子父组件通讯三
仍然是父组件展示数组信息,子组件展示输入框用于新增数据
父组件直接读取到子组件更新后的list
:父组件中定义了一个响应式变量childRef
,childRef
作为一个标记打到子组件标签上,就可以通过childRef获取到子组件的任何数据
子组件自己完成对list的更新,调用defineExpose
函数,指定list
的数据可以被外部访问
父组件代码:
<template> <Child ref="childRef"></Child> <div class="child"> <ul> <li v-for="item in childRef?.list">{{ item }}</li> </ul> </div> </template> <script setup> import Child from '@/components/child4.vue' import { ref, onMounted } from 'vue' const childRef = ref(null) </script>
子组件代码:
<template> <div class="inputGroup"> <input type="text" v-model="value"> <button @click="add">添加</button> </div> </template> <script setup> import { ref, defineProps } from 'vue' const value = ref('') const list = ref(['html', 'css', 'js']) const add = () => { list.value.push(value.value) } defineExpose({ list }) // 自愿暴露数据 </script>
ref
是 Vue 中的一个特殊属性,它允许我们在父组件中引用子组件或 DOM 元素
item in childRef?.list
中,?
是ES6的新语法,当childRef有值的时候才会读取后面的.list,没有的时候就不会去读取 (这样安排是因为父组件在执行这行代码的时候,子组件可能还未加载完毕,这样以免报错)
总结
- 父子组件通讯:父组件将值
v-bind
绑定传给子组件,子组件使用defineProps
接收 - 子组件向父组件通讯:借助发布订阅机制,子组件负责发布事件并携带参数,父组件订阅该事件,通过事件参数获取子组件提供的值
- 子组件向父组件通讯:父组件借助
v-model
将数据绑定给子组件,子组件创建'update:xxxx'
事件,并将接收到的数据修改后emits
出来 - 子组件向父组件通讯:父组件通过
ref
获取子组件中defineExpose()
暴露出来的数据
以上就是vue父子组件通讯的所有方法小结的详细内容,更多关于vue父子组件通讯的资料请关注脚本之家其它相关文章!