前言
在vue搭建项目的时候,会有父组件和子组件,你有没有思考一下当我想要得到父组件或者子组件的数据时,该怎么实现父组件和子组件之间的通讯呢?如果知道的话,那太nice啦,不太清楚的话那就往下看吧~
正文
我们通过实现下面的小demo来介绍父组件和子组件之间的通讯吧~
父组件传子组件
两种方式:
父组件中存放列表数据和input
,子组件来实现渲染列表,传列表给子组件
- 父组件通过
:list="list"
将list
传给子组件
ruby
<Child :list="list"></Child>
- 子组件通过
defineProps
接收父组件传入的list
:
php
defineProps({
list: {
type: Array,
default: () => [] // 默认值
}
});
App.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">添加</button>
</div>
<Child :list="list"></Child>
</template>
<script setup>
import Child from '@/components/child.vue';
import { ref } from 'vue';
const list = ref(['html', 'css', 'js']);
const value = ref('');
const add = () => {
list.value.push(value.value);
value.value = '';
}
</script>
<style lang="css" scoped>
</style>
child.vue
xml
<template>
<div class="child">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({
list: {
type: Array,
default: () => [] // 默认值
}
});
</script>
<style lang="css" scoped>
</style>
父组件存放input,子组件放列表并实现渲染,父组件传input内的值给子组件
- 父组件通过定义一个变量
toChild
,将value
赋值给toChild
- 通过
:msg="toChild"
将toChild
以msg
传递给子组件
ruby
<Child :msg="toChild"></Child>
- 子组件通过
defineProps
接收msg
ini
const props = defineProps({
msg:''
});
- 通过
watch
来监听msg
,对列表数据进行更新
scss
watch(
() => props.msg,
(newValue, oldValue) => {
list.value.push(newValue);
}
);
App.vue
ini
<template>
<div class="input-group">
<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 list = ref(['html', 'css', 'js']);
const value = ref('');
const toChild = ref('');
const add = () => {
toChild.value = value.value;
}
</script>
<style lang="css" scoped>
</style>
child.vue
xml
<template>
<div class="child">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import {ref, watch} from 'vue';
const list = ref(['html', 'css', 'js']);
const props = defineProps({
msg:''
});
watch(
() => props.msg,
(newValue, oldValue) => {
list.value.push(newValue);
}
);
</script>
<style lang="css" scoped>
</style>
子组件传父组件
子组件中存放input,父组件中存放数据清单并渲染数据
第一种方法
- 子组件创建一个
add1
事件
ini
const emits = defineEmits(['add1']); // 创建一个add事件
- 子组件在
add
函数中发布事件,并传入数据value
csharp
const add = () => {
// 将value给到父组件
emits('add1', value.value);// 发布事件
}
- 在父组件中订阅事件
add1
ini
<Child @add1="handle"></Child>
- 当触发
add1
事件时,也就是点击增加时触发handle
函数
csharp
const handle = (event) => {
list.value.push(event);
}
App.vue
xml
<template>
<!-- 订阅add1事件 -->
<Child @add1="handle"></Child>
<div class="child">
<ul>
<li v-for="(item, index) in list" :key="index">{{ 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) => {
list.value.push(event);
}
</script>
<style lang="css" scoped>
</style>
child.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const value = ref('');
const emits = defineEmits(['add1']); // 创建一个add事件
const add = () => {
// 将value给到父组件
emits('add1', value.value);// 发布事件
}
</script>
<style lang="css" scoped>
</style>
第二种方法
- 父组件通过
v-model:list="list"
将list传给子组件
ini
<Child v-model:list="list"></Child>
- 子组件通过
props
接收list
ini
const props = defineProps({
list:{
type: Array,
default: () => []
}
});
- 子组件可以直接在
add
函数通过props.list.push(value.value);
来实现对列表数据的更新,但是不太建议这种方法,不太建议直接操作父组件给过来的数据,这样的话代码的可维护性比较差 - 子组件通过使用
defineEmits
来新建一个emits
函数,该函数用于告知父组件,子组件希望更新一个名为list
的数据,再新建一个变量arr
来接收list
,来更新arr
,再通过emits('update:list', arr);
来实现对list的更新
ini
const emits = defineEmits(['update:list']);
const add = () => {
const arr = props.list;
arr.push(value.value);
emits('update:list', arr);
}
App.vue
xml
<template>
<Child v-model:list="list"></Child>
<div class="child">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import Child from '@/components/child3.vue';
import { ref } from 'vue';
const list = ref(['html', 'css', 'js']);
</script>
<style lang="css" scoped>
</style>
child.vue
ini
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const value = ref('');
const props = defineProps({
list:{
type: Array,
default: () => []
}
});
const emits = defineEmits(['update:list']);
const add = () => {
// props.list.push(value.value);// 不建议直接操作父组件给过来的数据
const arr = props.list;
arr.push(value.value);
emits('update:list', arr);
}
</script>
<style lang="css" scoped>
</style>
第三种方法
将列表数据存放在子组件中:
- 子组件通过
defineExpose({list});
将list
暴露出来 - 父组件通过
ref="childRef"
赋予了一个名字为childRef
的引用
ini
<Child ref="childRef"></Child>
- 父组件通过
const childRef = ref(null);
创建一个响应式的引用(ref)对象childRef - 通过
(item, index) in childRef?.list
来遍历列表数据
xml
<div class="child">
<ul>
<li v-for="(item, index) in childRef?.list" :key="index">{{ item }}</li>
</ul>
</div>
-childRef?.list
这是一种安全访问属性的方式,避免了尝试访问undefined
或null
对象的属性时抛出错误,也就是只有当childRef
不为null
或undefined
时,才会尝试访问其list
属性,因为此时子组件可能尚未被创建或挂载,直接访问childRef.list
会导致错误,因为null
或undefined
没有list
属性
App.vue
xml
<template>
<Child ref="childRef"></Child>
<div class="child">
<ul>
<li v-for="(item, index) in childRef?.list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import Child from '@/components/child4.vue';
import { ref } from 'vue';
const childRef = ref(null);
</script>
<style lang="css" scoped>
</style>
child.vue
xml
<template>
<div class="input-group">
<input type="text" v-model="value">
<button @click="add">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const value = ref('');
const list = ref(['html', 'css', 'js']);
const add = () => {
list.value.push(value.value);
}
defineExpose({list});// ==> defineExpose({list: list}); 心甘情愿暴露出list
</script>
<style lang="css" scoped>
</style>
结语
- 父子组件通讯 --- 父组件用v-bind绑定传给子组件,子组件用defineProps接收数据
- 子父组件通讯 --- 子组件用defineEmits创建一个事件,在发布该事件时传入数据,父组件通过v-on来订阅该事件
- 子父组件通讯 --- 父组件借助v-model将数据绑定给子组件,子组件创建'update:xxx'事件,并将接收到的数据修改后emits出来
- 子父组件通讯 --- 父组件通过ref获取子组件中的defineExpose()暴露出来的数据