在开发中,发现了一些常用的vue中组件通信的方法,接下来我将为大家介绍下,如有错误请及时批评指正。
通信方法
- 父组件给子组件传值
- 父组件用v-bind传递数据给子组件,子组件用defineProps接收数据。
- 父组件通过provide传递数据,子组件通过inject注入。
- 子组件给父组件传值
- 子组件用defineEmitd定义事件发布,父组件用v-on订阅事件。
- 子组件通过defineExpose暴露数据,父组件通过ref引用组件中的数据
- 父组件通过v-model,子组件通过defineProps接受,然后定义'update:xxx'事件,并修改父组件给送过来数据,但一定是要发布'update:xxx'
- 兄弟组件通信
- 在外部的js文件中定义响应式变量,同时引入到两个组件中,因为是响应式的,所以两个组件都可以修改这给变量,从而实现通信。
组件通信的写法
父传子
我们要写一个输入框和一个点击按钮,点击按钮后,会将输入框中的内容添加到数组,放入子组件中展示。
- 通过在父组件中通过属性绑定
v-bind
将数据传递给子组件,子组件通过defineProps
声明Props来接受数据。
js
<template>
<div class="head">
<input type="text" v-model="msg">
<button @click="add">添加</button>
</div>
<Child :data="data" />
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const msg = ref('')
const data = ref('')//这里定义一个data,来保证传入的数据在点击添加才会改变,不被msg的值的改变影响
const add = () => {
data.value = msg.value
}
</script>
<style lang="css" scoped></style>
js
<template>
<div class="body">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
data: {
type: String,
default: ''
}
})
const list = ref(['html', 'css', 'js'])
watch(() => props.data, (newVal) => { //监听如果传入的数据有改变,添加进入数组
if (newVal) {
list.value.push(newVal)
}
})
</script>
<style lang="css" scoped></style>
这种写法并不是很好,将数据放在子组件中储存,会导致子组件的复用性降低,比如写一个展示数据的组件,放在主页能用,放到展示其他数据的页面就不行了。最好是将数据放在父组件中,子组件只发出更改的请求。可以根据项目需求使用,需要重复用的子组件最好别这样写。
- 父组件通过provide数据,子组件通过inject接收父组件传来的数据。provide有穿透的效果,在子组件的子组件中也能收到父组件传来的值。用readonly冻结数据,防止在子组件中修改。
js
<template>
<div class="head">
<input type="text" v-model="msg">
<button @click="add">添加</button>
</div>
<Child />
</template>
<script setup>
import Child from './child.vue'
import { ref, provide, readonly } from 'vue'
const msg = ref('')
const list = ref(['html', 'css', 'js'])
const add = () => {
list.value.push(msg.value)
console.log(list.value);
}
provide('list', readonly(list.value)) // 向下提供数据,用readonly防止子组件被修改
</script>
<style lang="css" scoped></style>
js
<template>
<div class="body">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { inject } from 'vue'
const list = inject('list') // 注入数据
</script>
<style lang="css" scoped></style>
子传父
- 子组件用defineEmitd 定义事件并发布,父组件用v-on订阅事件。在子组件中发布事件,在父组件中接受数据。
js
<template>
<Child @add="handle" />//这里定义了函数,来接受子组件的add事件(子组件发布的事件)
<div class="body">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const list = ref(['html', 'css', 'js'])
const handle = (val) => {
list.value.push(val)
}
</script>
<style lang="css" scoped></style>
js
<template>
<div class="head">
<input type="text" v-model="msg">
<button @click="add">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const msg = ref('')
const emit = defineEmits(['add']) // 定义事件
const add = () => {
emit('add', msg.value)
}
</script>
<style lang="css" scoped></style>
- 子组件通过defineExpose暴露数据,父组件通过ref引用组件中的数据.子组件将数据放到全局,让所有组件都可以拿到。
js
<template>
<Child ref="childRef" />
<div class="body">
<ul>
<li v-for="(item, index) in childRef?.list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const childRef = ref(null)
setTimeout(() => {
console.log(childRef.value.list)
}, 1000)
</script>
<style lang="css" scoped></style>
js
<template>
<div class="head">
<input type="text" v-model="msg">
<button @click="add">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const msg = ref('')
const list = ref(['html', 'css', 'js'])
const add = () => {
list.value.push(msg.value)
}
// 暴露数据
defineExpose({
list
})
</script>
<style lang="css" scoped></style>
- 父组件通过v-model,子组件通过defineProps接受,然后定义'update:xxx'事件,并修改父组件给送过来数据,但一定是要发布'update:xxx'。其实是一个语法糖,defineProps在子组件接受父组件的数据,修改好了后,用defineEmits传回给父组件,完成通信。
js
<Child v-model:list="list" />//等于下面的写法
<Child v-bind:list="list" @update:title="pageTitle = $event"/>//v-bind用于接受父组件传来的数据,@updata在数据改变这个事件后,把数据从子组件传回给父组件。
js
<template>
<div class="head">
<input type="text" v-model="msg">
<button @click="add">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const msg = ref('')
const props = defineProps(['list'])
const emits = defineEmits(['update:list'])
const add = () => {
const arr = props.list
arr.push(msg.value)
emits('update:list', arr)
}
</script>
<style lang="css" scoped></style>
js
<template>
<Child v-model:list="list" />
<div class="body">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import Child from './child.vue'
import { ref } from 'vue'
const list = ref(['html', 'css', 'js'])
</script>
<style lang="css" scoped></style>
兄弟通信
在外部的js文件中定义响应式变量,同时引入到两个组件中,因为是响应式的,所以两个组件都可以修改这个变量,从而实现通信。其实用Pinian这种工具也行。
js
<template>
<div class="body">
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { list } from './bus.js'
</script>
<style lang="css" scoped></style>
js
<template>
<div class="head">
<input type="text" v-model="msg">
<button @click="add">添加</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { list } from './bus.js'
const msg = ref('')
const add = () => {
list.value.push(msg.value)
}
</script>
<style lang="css" scoped></style>
js
<template>
<Head />
<Body />
</template>
<script setup>
import Head from './head.vue';
import Body from './body.vue';
</script>
<style lang="css" scoped></style>
js文件
js
import { ref } from 'vue'
export const list = ref(['html', 'css', 'js'])