Value transfer logic between sub-components in vue3 combined API <script setup>

The following seven component communication methods are introduced below:

  • props
  • emit
  • v-model
  • refs
  • provide/inject
  • eventBus

1. Props

The parent component code is as follows:

<template>
  <!-- child component -->
  <child-components :list="list"></child-components>
  
  <!-- parent component -->
  <button @click="handleAdd"  type="button">add</button>

</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])


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

The child component only needs to render the value passed by the parent component. code show as below:

<template>
  <ul>
    <li v-for="i in props.list" :key="i">{
   
   { i }}</li>
  </ul>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
  list: {
    type: Array,
    default: () => [],
  },
})
</script>

2. Emit

The subcomponent code is as follows:

<template>
  <div>
    <input v-model="value" type="text"/>
    <div>
      <button @click="handleSubmit" type="button">add</button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineEmits } from 'vue'
const value = ref('')
const emits = defineEmits(['add'])
const handleSubmit = () => {
  emits('add', value.value)
  value.value = ''
}
</script>
  • After clicking the [Add] button in the child component, we will emit a custom event and pass the added value as a parameter to the parent component.

The parent component code is as follows:

<template>
  <!-- parent component -->
  <ul>
    <li v-for="i in list" :key="i">{
   
   { i }}</li>
  </ul>
  <!-- child component -->
  <child-components @add="handleAdd"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])

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

In the parent component, you only need to listen to the custom events of the child component and then execute the corresponding addition logic.

3. v-model

v-model is an excellent syntax sugar in Vue , such as the following code.

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

This is the shorthand form of the following code

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

Subassembly

In the subcomponent, we first define props and emits, and then emit the specified event after adding them.

Note: update:* is a fixed writing method in Vue, * represents a property name in props.

It is relatively simple to use in the parent component, the code is as follows:

<template>
  <!-- parent component -->
  <ul>
    <li v-for="i in list" :key="i">{
   
   { i }}</li>
  </ul>
  <!-- child component -->
  <child-components v-model:list="list"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
</script>

4. Refs

When using API options, we can get the specified element or component through this.$refs.name, but not in the composition API. If we want to obtain it through ref, we need to define a Ref object with the same name, which can be accessed after the component is mounted.

The sample code is as follows:

<template>
  <ul>
    <li v-for="i in childRefs?.list" :key="i">
      {
   
   { i }}
    </li>
  </ul>
 
  <child-components ref="childRefs"></child-components>
  <!-- parent component -->
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const childRefs = ref(null)
</script>

The subcomponent code is as follows:

<template>
  <div>
    <input v-model="value" type="text"/>
    <div>
      <button @click="handleAdd" type="button">add</button>
    </div>
  </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>

Note: By default, the setup component is closed, and the public instance of the component is obtained through the template ref. If it needs to be exposed, it needs to be exposed through defineExpose  API.

5. provide/inject

provide/inject is a pair of APIs provided in Vue. No matter how deep the hierarchy is, the API can realize data transfer from parent components to child components.

parent component

<template>
  <!-- child component -->
  <child-components></child-components>
  <!-- parent component -->
  <div>
    <input v-model="value" type="text"/>
    <div>
      <button @click="handleAdd" type="button">add</button>
    </div>
  </div>
</template>
<script setup>
import { ref, provide } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')

provide('list', list.value)

const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
</script>
  • Subassembly
<template>
  <ul>
    <li v-for="i in list" :key="i">{
   
   { i }}</li>
  </ul>
</template>
<script setup>
import { inject } from 'vue'

const list = inject('list')
</script>

Note: When using provide for data transmission, try to use readonly to encapsulate the data to prevent child components from modifying the data passed by the parent component.

6. eventBus

eventBus has been removed in Vue 3, but it can be done with the help of third-party tools. Vue officially recommends using mitt or tiny-emitter. In most cases, it is not recommended to use a global event bus for component communication. Although relatively simple and crude, maintaining the event bus is a big problem in the long run.

  • First, you need to install  mitt :
npm install --save mitt
  • Then  libs create a file in the folder  bus.ts . The content is actually the same as the old version, except that the Vue instance is replaced by a mitt instance.
import mitt from 'mitt';
export default mitt();
  • Create and remove listening events

In the components that need to expose communication events,  on the receiving method is configured. At the same time, in order to avoid the event being bound and triggered multiple times during the routing switching process, it needs to be  off removed at the appropriate time:

import { defineComponent, onBeforeUnmount } from 'vue'
import bus from '@libs/bus'

export default defineComponent({
  setup () {
    // 定义一个打招呼的方法
    const sayHi = (msg: string = 'Hello World!'): void => {
      console.log(msg);
    }

    // 启用监听
    bus.on('sayHi', sayHi);

    // 在组件卸载之前移除监听
    onBeforeUnmount( () => {
      bus.off('sayHi', sayHi);
    })
  }
})
  • Call listening event

In the component that needs to call the communication event,  emit call:

import { defineComponent } from 'vue'
import bus from '@libs/bus'

export default defineComponent({
  setup () {
    // 调用打招呼事件,传入消息内容
    bus.emit('sayHi', '哈哈哈哈哈哈哈哈哈哈哈哈哈哈');
  }
})

Upgrade EventBus for old projects

In the EventBus of Vue 3.x, we can see that its API is very close to the old version, except that  $ the symbols are removed.

If you want to upgrade an old project, because it originally used the  $on old  $emit API, it is definitely not realistic to modify the components one by one to the new API.

We can  mount the API bus.ts by customizing an  bus object  when creating it mitt .

Here  bus.ts , change to the following code:

import mitt from 'mitt';

// 初始化一个 mitt 实例
const emitter = mitt();

// 定义一个空对象用来承载我们的自定义方法
const bus: any = {};

// 把你要用到的方法添加到 bus 对象上
bus.$on = emitter.on;
bus.$emit = emitter.emit;

// 最终是暴露自己定义的 bus
export default bus;

bus.$on In this way, we can continue to use and bus.$emit wait for the old API in the component  without affecting the upgrade and use of our old projects.

Guess you like

Origin blog.csdn.net/weixin_62635213/article/details/131641608
Recommended