在写 vue3 的项目中,我们都会进行组件通信,除了使用 pinia 公共数据源的方式除外,我们还可采用那些更简单的API方法呢?给大家介绍介绍几种父子组件和子父组件通信的方式。
1、父子组件通信
1.1 defineProps
父子组件通信我们第一个想到的就是props,我们在子组件显示声明所接受的props,然后我们在从父组件传入对应的 key与value, 这样我们就可以在子组件上接收到父组件传过来的属性与值。
具体实现如下:
// children.vue
<template>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="index">
{{item}}
</li>
</ul>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
list :{
type: Array,
default: () => {}
}
})
</script>
// parent.vue
<template>
<div class="parent-wrap">
<input type="text" v-model="value" class="form-control" placeholder="请输入">
<div class="input-group-append">
<button class="btn btn-primary" @click="handleAdd">添加</button>
</div>
</div>
<!-- child -->
<childrenVue :list="list"></childrenVue>
</template>
<script setup>
import { ref } from 'vue';
import childrenVue from './children.vue';
const value = ref('')
const list = ref(['javaScript', 'Html', 'CSS'])
const handleAdd = () =>{
list.value.push(value.value)
value = ''
}
</script>
如上图所示,我们既实现了在子组件上显示了父组件传过来的 list 数组,还使可以向list添加数据使子组件数据更新。
1.2 provide/inject
当我们聊完了props,我们第二个要介绍的就是 vue3 的一个组合式选项 provide 和inject。
projct用于提供可以被后代组件注入的值,而inject用于声明要通过从上层提供方匹配并注入进当前组件的属性。 其代码实现如下:
// children.vue
<template>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item">{{item}}</li>
</ul>
</template>
<script setup>
import { inject } from 'vue';
const list = inject('list')
</script>
// parent.vue
<template>
<div class="parent-wrap">
<input type="text" v-model="value" class="form-control" placeholder="请输入">
<div class="input-group-append">
<button class="btn btn-primary" @click="handleAdd">添加</button>
</div>
</div>
<!-- child -->
<childVue />
</template>
<script setup>
import childVue from "./child.vue";
const { ref, provide, readonly } = require("vue");
const value = ref('')
const list = ref(['javaScript', 'HTML', 'CSS'])
provide('list', readonly(list.value))
const handleAdd = () => {
list.value.push(value.value)
}
</script>
如上图所示,我们使用 provide
API向外提供了一个 key 为 list
,值为list.value
,同时将 list,value
设置成了只读属性,防止子组件修改父组件的数据源。然后我们 inject
API接收了 list
,实现了父子组件的通信。
2.子父组件通信
2.1 defineEmits
上面我介绍了两种父向子传值的方法,但在我们开发中,我们还会遇到子向父组件传值的情况,那我们该怎么解决呢? 第一个方法就是vue3中的 defineEmits
API,代码实现如下:
// children.vue
<template>
<div class="parent-wrap">
<input type="text" v-model="value" class="form-control" placeholder="请输入" />
<div class="input-group-append">
<button class="btn btn-primary" @click="handleAdd">添加</button>
</div>
</div>
</template>
<script setup>
const { ref, defineEmits } = require("vue");
const value = ref('')
const emits = defineEmits(['add']) //父传子
// 给父组件传一个函数
const handleAdd = () => {
emits('add', value.value)
value.value= ''
}
</script>
// parent.vue
<template>
<childVue @add='handleAdd'/>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item">{{item}}</li>
</ul>
</template>
<script setup>
import { ref } from '@vue/reactivity';
import childVue from './child.vue';
const list = ref(['javaScript', 'HTML', 'CSS'])
const handleAdd = (val) => {
list.value.push(val)
}
</script>
如上图所示,我们在子组件上emit
一个出了一个 add事件给父组件接收,同时在父组件上调用来执行添加的逻辑,再将 input
的value
变为空,实现了父组件向子组件传参。
2.2 v-model:xxx + emit
在介绍完 defineEmits
后, 我们再来介绍一种与其有异曲同工之处的v-model:xxx + emit
的方法,实现如下:
// children.vue
<template>
<div class="parent-wrap">
<input type="text" v-model="value" class="form-control" placeholder="请输入" />
<div class="input-group-append">
<button class="btn btn-primary" @click="handleAdd">添加</button>
</div>
</div>
</template>
<script setup>
const { ref, defineProps, defineEmits } = require("vue");
const value = ref('')
const props = defineProps({
list: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['list'])
// 给父组件一点东西
const handleAdd = () => {
// props.list.push(value.value) //不建议直接修改props的值 把握不住数据源的流转
const arr = props.list
arr.push(value.value)
emits('list', arr)
value.value= ''
}
</script>
<template>
<childVue v-model:list="list" @list ='add'/>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item">{{item}}</li>
</ul>
</template>
<script setup>
import { ref } from '@vue/reactivity';
import childVue from './child.vue';
const list = ref(['javaScript', 'HTML', 'CSS'])
const add =(val) => {
console.log(val);
console.log(list);
}
</script>
再和上面的defineEmits
方法比较完以后,相信大家也看出了这两者的异曲同工在哪了。我们这里是先将父组件的list
传给了子组件,再在子组件修改了父组件的数据源,同时再emit
还给父组件,实现了子组件向父组件传值。