vue3 基于 setup 的组件通信


前言

vue2 的组件间的通信可戳这里

一、父子组件通信

1、父传子——defineProps

父组件:

<template>
  <children :list="list"></children>
  <div>
    <input v-model="value" type="text" placeholder="请输入" />
    <button @click="handleAdd"> 添加 </button>
  </div>
</template>
<script setup>
  import { ref } from 'vue'
  import Children from '@/views/parentChildCommunicat/parentToChildren/components/children.vue'

  const list = ref(['JavaScript', 'HTML', 'CSS'])
  const value = ref('')

  const handleAdd = () => {
    list.value.push(value.value)
    value.value = ''
  }
</script>

子组件:

<template>
  <ul>
    <li v-for="item in props.list" :key="item">{{ item }}</li>
  </ul>
</template>
<script setup>
  import { defineProps } from 'vue'

  const props = defineProps({
    list: {
      type: Array,
      default: () => []
    }
  })
</script>

2、子传父——defineEmits

子组件:

<template>
  <div>
    <input v-model="value" type="text" placeholder="请输入" />
    <button @click="handleAdd"> 添加 </button>
  </div>
</template>
<script setup>
  import { ref, defineEmits } from 'vue'

  const value = ref('')
  const emits = defineEmits(['add'])

  const handleAdd = () => {
    emits('add', value.value)
    value.value = ''
  }
</script>

父组件:

<template>
  <ul>
    <li v-for="item in list" :key="item">{{ item }}</li>
  </ul>
  <son @add="add" />
</template>
<script setup>
  import { ref } from 'vue'
  import Son from './components/son.vue'

  const list = ref(['JavaScript', 'HTML', 'CSS'])

  const add = (value) => {
    list.value.push(value)
  }
</script>

3、v-model 实现父子组件双向通信

v-model 是 Vue 中一个比较出色的语法糖,比如:

对于代码:

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

使用 v-model 可以简写为:

<ChildComponent v-model:title="pageTitle" />

下面就来看看如何用 v-model 实现父子组件双向通信。

子组件:

<template>
  <div>
    <input v-model="value" type="text" placeholder="请输入" />
    <button @click="handleAdd"> 添加 </button>
  </div>
</template>
<script setup>
  import { ref, defineEmits, defineProps } from 'vue'

  const value = ref('')
  const props = defineProps({
    list: {
      type: Array,
      default: () => []
    }
  })

  const emits = defineEmits(['update:list'])

  const handleAdd = () => {
    const arr = props.list
    arr.push(value.value)
    emits('update:list', arr)
    value.value = ''
  }
</script>

父组件:

<template>
  <ul>
    <li v-for="i in list" :key="i">{{ i }}</li>
  </ul>
  <child-components v-model:list="list" />
</template>
<script setup>
  import { ref } from 'vue'
  import ChildComponents from './components/child-components.vue'

  const list = ref(['JavaScript', 'HTML', 'CSS'])
</script>

二、跨级组件通信

1、refs 实现组件间的通信(可跨级)——defineExpose

在使用 “选项式 API” 时,可以通过 this.$refs.name 的方式获取指定元素或者组件。
在 “组合式 API” 中,必须通过定义一个同名的 Ref 响应式对象,在组件挂载后,才可以通过 ref 的方式获取组件或者元素。

父组件:

<template>
  <ul>
    <li v-for="item in childRefs?.list" :key="item">{{ item }}</li>
  </ul>
  <children-refs ref="childRefs" />
</template>
<script setup>
  import { ref } from 'vue'
  import ChildrenRefs from './components/children-refs.vue'

  const childRefs = ref(null)
</script>

子组件:

<template>
  <div>
    <input v-model="value" type="text" placeholder="请输入" />
    <button @click="handleAdd"> 添加 </button>
  </div>
</template>
<script setup>
  import { ref, defineExpose } from 'vue'

  const list = ref(['JavaScript', 'HTML', 'CSS'])
  const value = ref('')

  const handleAdd = () => {
    list.value.push(value.value)
    value.value = ''
  }
  defineExpose({ list })
</script>

【注意】setup 组件默认是关闭的,也就是说:通过模板 ref 获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。若要公开则必须使用 defineExpose 来对外暴露。

2、Provide + Inject 组件通信(可跨级)

provide 和 inject 是 Vue 中提供的一对 API。该 API 可以实现:祖先组件向子孙组件传递数据(无论层级有多深),包括父子组件传值。

(1)、基于 Provide + Inject 的基本通信

父组件:

<template>
  <son-provide-inject />
  <div>
    <input v-model="value" type="text" placeholder="请输入" />
    <button @click="handleAdd"> 添加 </button>
  </div>
</template>
<script setup>
  import { ref, provide, readonly } from 'vue'
  import SonProvideInject from './components/son-provide-inject.vue'

  const list = ref(['JavaScript', 'HTML', 'CSS'])
  const value = ref('')
  
  // 将数据置为只读
  const readonlyList = readonly(list)
  // 向子组件提供数据
  provide('list', readonlyList.value)

  const handleAdd = () => {
    list.value.push(value.value)
    value.value = ''
  }
</script>

子组件:

<template>
  <ul>
    <li v-for="item in list" :key="item">{{ item }}</li>
  </ul>
</template>
<script setup>
  import { inject } from 'vue'

  // 接受父组件提供的数据
  const list = inject('list')
</script>

【注意】使用 provide 进行数据传递时,尽量用 readonly 将数据置为只读,以避免子组件修改父级传递过去的数据。

(2)、基于 Provide + Inject 的异步通信

基于 Provide + Inject 的异步通信 与 基本通信的区别:主要是 provide 里的变动。

父组件:

<template>
  <son-provide-inject-async />
  <button @click="changeStatus"> 添加 </button>
</template>
<script setup>
  import { ref, provide, computed } from 'vue'
  import SonProvideInjectAsync from './components/son-provide-inject-async.vue'

  const flag = ref(false)

  // 向子组件提供数据
  provide(
    'asyncFlag',
    computed(() => flag.value)
  )
  // 异步数据
  const changeStatus = () => {
    setTimeout(() => {
      flag.value = true
    }, 3000)
  }
</script>

子组件:

<template>
  <div>{{ asyncFlag }}</div>
</template>
<script setup>
  import { inject } from 'vue'

  // 接受父组件提供的数据
  const asyncFlag = inject('asyncFlag')
</script>

3、Vue3 借助 mitt 来实现组件间的通信——事件总线

Vue3 中移除了“事件总线”,但是可以借助于第三方工具来完成,Vue官方推荐 mitttiny-emitter。本小节只说下 mitt。

一般不推荐使用全局事件总线的方式来实现组件通信,虽然比较简单粗暴,但是长久来说维护事件总线是一个大难题。

安装 mitt:

npm i -S mitt

在 src/utils 目录下新建 bus.js 文件:

import mitt from 'mitt'

const emiiter = mitt()

export default emiiter

使用 mitt 进行兄弟组件的通信:

<template>
  <p>这是A页面</p>
  <button @click="btn">点击</button>
</template>
 
<script setup>
import emitter from "@/utils/bus";
 
const str = ref("我是A组件的数据");
const btn = () => {
  emitter.emit("fn", str);
};
</script>
<template>
  <p>这是B页面</p>
  {{ s }}
</template>
 
<script setup>
import emitter from "@/utils/bus";
 
const s = ref(""); // 等待接收
const handelEventFn = (e) => {
  s.value = e.value;
};
onBeforeMount(() => {
  emitter.on("fn", handelEventFn); // 开启监听,监听到fn事件后,执行handelEventFn函数
});
onUnmounted(() => {
  emitter.off("fn"); //  取消fn事件的全部处理函数的监听
});
</script>

4、使用状态管理工具——Vuex 或 Pinia

Vuex 官网
Pinia 官网

Vuex 和 Pinia 是可用于 Vue3 中的状态管理工具,使用这两个工具都可以实现各种组件之间的通信。

三、子组件更新父组件的变量
1、修改父组件的 基本类型的数据
(1)、v-model
父组件中

const test1 = ref('a')

// 使用watchEffect监听test的变化
watchEffect(() => {
    console.log('Test1的值发生了变化:', test1.value);
});

子组件中

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const value = computed({
    get() {
        return props.modelValue
    },
    set(value) {
        emit('update:modelValue', value)
    }
})
value.value = 'A'

2、修改父组件的 引用类型的数据




【参考】
总结了 Vue3 的七种组件通信方式,别再说不会组件通信了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值