Vue父子组件通信的6种方式,props、emits、def
一、引言
Vue采用组件化开发模式,将一个页面拆分成多个组件,每个组件都有自己的数据和方法。在实际开发中,经常需要对组件之间进行通信,以实现数据共享或事件传递的功能,本文将会介绍Vue中组件通信的6种方式:props、emits、ref/defineExpose、v-model、provide/inject、attrs。
二、props
props通过在子组件上绑定属性来实现父子组件之间的数据传递。父组件将数据作为prop传递给子组件,子组件在自己的作用域中可以访问和使用这些prop。
为节省篇幅,css代码就不放出来了,能说明通信的方式就行。
<!-- App.vue -->
<template>
<h1>父组件</h1>
<div class="input-group">
<input type="text" v-model="val">
<button @click="handle">发送数据</button>
</div>
<Child :msg="list"/>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/Child1-1.vue';
const val = ref("");
const list = ref(['666']);
const handle = () => {
list.value.push(val.value);
}
</script>
<!-- Child1-1.vue -->
<template>
<h1>子组件</h1>
<ul class="list">
<li class="list-item"
v-for="(item, index) in props.msg" :key="index">
{{ item }}
</li>
</ul>
</template>
<script setup>
const props = defineProps({
msg: {
type: Array,
default: () => []
}
})
</script>
效果图:
三、emits
emits通过在子组件中声明可触发的事件
,使得子组件可以在适当的时机触发这些事件。父组件可以在子组件上监听这些事件,并在触发时执行相应的处理逻辑。这种方式实现了子组件向父组件的通信
,子组件可以通过事件来向父组件传递信息或请求。
<!-- App2.vue -->
<template>
<Child @add="handleAdd"/>
<h1>父组件</h1>
<ul class="list">
<li class="list-item"
v-for="(item, index) in list" :key="index">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/Child2-1.vue';
const list = ref(['默认测试数据'])
const handleAdd = (val) => {
list.value.push(val);
}
</script>
<!-- Child2-1.vue -->
<template>
<h1>子组件</h1>
<div class="input-group">
<input type="text" v-model="val">
<button @click="handle">发送数据</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const val = ref("");
const emits = defineEmits(['add']) //发布事件
const handle = () => {
emits('add', val.value)
}
</script>
四、ref/defineExpose
ref/defineExpose通过使用ref将数据或方法包装
,并使用defineExpose将其暴露
给父组件。父组件可以通过在子组件上使用ref来访问这些暴露出来的数据或方法。这种方式使得父组件可以直接访问和操作子组件的内部状态。
childRef?.msg
这里的问号是等childRef
数据加载完毕后才去取数据,否则这里可能取不到值,这里需要考虑到父组件和子组件的生命周期。
<!-- App4.vue -->
<template>
<Child ref="childRef"/>
<h1>父组件</h1>
<ul class="list">
<li class="list-item"
v-for="(item, index) in childRef?.msg" :key="index">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/Child4.vue';
const childRef = ref("");
</script>
<!-- Child4.vue -->
<template>
<h1>子组件</h1>
<div class="input-group">
<input type="text" v-model="val">
<button @click="handle">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const val = ref('')
const list = ref(['默认测试数据!'])
const handle = () => {
list.value.push(val.value);
}
defineExpose({msg:list})
</script>
五、v-model
v-model是通过将属性和事件绑定到同一个值来实现双向数据绑定。相当于父组件将自己的数据源交给子组件处理,父组件里不需要写什么代码,操作大都放到子组件里去写。
<!-- App3.vue -->
<template>
<Child v-model:msg="list"/>
<h1>父组件</h1>
<ul class="list">
<li class="list-item"
v-for="(item, index) in list"
:key="index">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/Child3.vue';
const list = ref(['默认测试数据!'])
</script>
<!-- Child3.vue -->
<template>
<h1>子组件</h1>
<div class="input-group">
<input type="text" v-model="val">
<button @click="handle">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
msg:Array
})
const val = ref('')
const emits = defineEmits(['update:msg'])
const handle = () => {
const arr = props.msg;
arr.push(val.value);
emits('update:msg', arr);
}
</script>
六、provide/inject
provide/inject通过在祖先组件中使用provide提供数据或方法,并在后代组件中使用inject来注入这些数据或方法。不仅仅是子组件、孙子组件或曾孙组件等都可以直接使用inject接收到父组件传递的数据。
<!-- App5.vue -->
<template>
<h1>父组件5</h1>
<div class="input-group">
<input type="text" v-model="val">
<button @click="handle">添加</button>
</div>
<Child/>
</template>
<script setup>
import { ref, provide } from 'vue';
import Child from './components/Child5.vue';
const val = ref('')
const list = ref(['默认测试数据!'])
const handle = () => {
list.value.push(val.value);
}
provide("list", list.value);
</script>
<!-- Child5.vue -->
<template>
<h1>子组件5</h1>
<ul class="list">
<li class="list-item"
v-for="(item, index) in testList" :key="index">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { inject } from 'vue';
const testList = inject("list");
</script>
七、attrs
attrs用于子组件接收父组件中不是通过props接收的属性。父组件给子组件传递属性,可以是自定义属性,这对于在子组件中将额外的prop传递给子元素或子组件比较有用。
<!-- App6.vue -->
<template>
<h1>父组件 传送数据</h1>
<Child :fruit="data1" :type="data2"/>
</template>
<script setup>
import { ref } from 'vue';
import Child from './components/Child6.vue';
const data1 = ref("水果");
const data2 = ref("哈密瓜");
</script>
<!-- Child6.vue -->
<template>
<h1>子组件 接收数据</h1>
<p>子组件通过props收到的是: {{props.fruit}}</p>
<p>子组件通过attrs收到的是: {{attrs.type}}</p>
</template>
<script setup>
import { useAttrs } from 'vue';
const props = defineProps({
fruit: {
type: String,
default: ""
}
})
const attrs = useAttrs();
console.log(attrs);
</script>
八、最后的话
这里梳理的都是父子组件之间的通信,如果要兄弟组件之间传值,大家可以去用仓库(store
),vuex
和pinia
都不错!
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态。Pinia 是 Vue 的存储库,可以用来跨组件/页面共享状态。
能力一般,水平有限,本文可能存在纰漏或错误,如有问题欢迎大佬指正,感谢你阅读这篇文章,如果你觉得写得还行的话,不要忘记点赞、评论、收藏哦!祝生活愉快!