Vue3's seven component communication modes, no more component communication

This article is all adopted <script setup> This combined API writing method is more free than the option API writing method. For details, please refer to Vue documents Description of two ways.

This article will introduce the following seven component communication modes:

  • props
  • emit
  • v-model
  • refs
  • provide/inject
  • eventBus
  • Vuex / Pinia (state management tool)

Start making things~

Props mode

Props is the most common method of parent-child transmission in Vue, and it is also relatively simple to use.

According to the above demo, we define the data and operations on the data in the parent component, and the child component only renders the list;

The parent component code is as follows:

<template>
  <!-- Subcomponents -->
  <child-components :list="list"></child-components>
  <!-- Parent component -->
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="Please enter"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        add to
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// Event handling function triggered by add
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
</script>

The sub component only needs to render the value passed by the parent component. The code is as follows:

<template>
  <ul class="parent list-group">
    <li class="list-group-item" 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>

emit mode

The emit mode is also the most common component communication mode in Vue, which is used to pass the child to the parent;

According to the above demo, we define the list in the parent component, and the child component only needs to pass the added value.

The sub component codes are as follows:

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="Please enter"
    />
    <div class="input-group-append">
      <button @click="handleSubmit" class="btn btn-primary" type="button">
        add to
      </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>
Copy code

After clicking the [add] button in the sub component, emit a user-defined event and pass the added value as a parameter.

The parent component code is as follows:

<template>
  <!-- Parent component -->
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
  <!-- Subcomponents -->
  <child-components @add="handleAdd"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
// Event handling function triggered by add
const handleAdd = value => {
  list.value.push(value)
}
</script>

Copy code

In the parent component, you only need to listen to the events defined by the child component, and then perform the corresponding add operation.

v-model mode

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

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

Copy code

This is the abbreviation of the following code

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

v-model is really much simpler. Now let's take a look at the above demo and how to implement it with v-model.

Subcomponents

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="Please enter"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        add to
      </button>
    </div>
  </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'])
// Add operation
const handleAdd = () => {
  const arr = props.list
  arr.push(value.value)
  emits('update:list', arr)
  value.value = ''
}
</script>

Copy code

In the subcomponent, we first define props and emit s, and then specify the event after adding.

Note: update: * refers to the fixed writing method in Vue, and * refers to an attribute name in props.

It is easy to use in the parent component. The code is as follows:

<template>
  <!-- Parent component -->
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
  <!-- Subcomponents -->
  <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>

Copy code

refs mode

When using the optional API, we can use this$ refs. Name is used to obtain the specified element or component, but the combination API cannot use which method to obtain it. If we want to obtain components or elements by ref, we need to define a ref object with the same name, which can be accessed after the component is mounted.

The example code is as follows:

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in childRefs?.list" :key="i">
      {{ i }}
    </li>
  </ul>
  <!-- Subcomponents ref Value and<script>Consistent in -->
  <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>

Copy code

The sub component codes are as follows:

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="Please enter"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        add to
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineExpose } from 'vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// Event handling function triggered by add
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
defineExpose({ list })
</script>

Copy code

The setup component is closed by default, that is, the public instance of the component obtained through the template ref will not expose any binding declared in * * < script setup >. If it needs to be disclosed, it needs to expose * * through * * * * defineExpose**** API.

provide/inject mode

provide and inject are a pair of APIs provided in Vue, which can transfer data from parent components to child components. No matter how deep the level is, they can be implemented through this pair of APIs. The example code is as follows:

Parent component

<template>
  <!-- Subcomponents -->
  <child-components></child-components>
  <!-- Parent component -->
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="Please enter"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        add to
      </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 data to subcomponents
provide('list', list.value)
// Event handling function triggered by add
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
</script>

Copy code

Subcomponents

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
</template>
<script setup>
import { inject } from 'vue'
// Accept data provided by parent component
const list = inject('list')
</script>
Copy code

It is worth noting that when using provide for data transmission, try to wrap the data readonly to avoid the child component modifying the data passed by the parent.

Event bus

The event bus is removed from Vue3, but it can be completed with the help of third-party tools, which is officially recommended by Vue mitt or tiny-emitter

In most cases, it is not recommended to use the global event bus to realize component communication. Although it is relatively simple and rough, maintaining the event bus is a big problem for a long time, so we won't explain it here. For details, you can read the documentation of specific tools

Status management tool

Vuex and Pinia It is a state management tool in Vue3. Using these two tools can easily realize component communication. Because these two tools are powerful, they will not be shown here. For details, you can refer to the documents

Write at the end

This article ends here. Generally speaking, it is relatively simple and has no complex content.

If this article is of some use to you, you are welcome to like, comment and collect it to avoid missing it when you need it.

If there are mistakes in the text, please correct them~

Keywords: Javascript Vue Vue.js elementUI

Added by evan18h on Wed, 16 Feb 2022 11:12:56 +0200