Frontend Development 14 min read

13 Ways to Pass Data Between Components in Vue 3

This article comprehensively outlines thirteen distinct methods for component communication in Vue 3, covering parent‑to‑child props, child‑to‑parent emits, sibling communication via mitt, $attrs, refs, v‑model, provide/inject, router parameters, Vuex, Pinia, browser storage, window object, and globalProperties, with concise code examples for each.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
13 Ways to Pass Data Between Components in Vue 3

Introduction

Vue 3 has been stable for a long time. This article lists thirteen ways to pass data between components, using the most concise <script setup> syntax for each method.

1. Parent to Child (props)

Parent component binds a variable with a colon, child receives it via defineProps .

<template>
  <child :name="name"></child>
</template>

<script setup>
import { ref } from 'vue'
import child from './child.vue'

const name = ref('天天鸭')
</script>

Child component:

<template>
  <div>{{ props.name }}</div>
</template>

<script setup>
import { defineProps } from 'vue'
const props = defineProps({
  name: {
    type: String,
    default: '',
  },
})
</script>

2. Child to Parent (emit)

Child registers an event with defineEmits and triggers it, passing data to the parent.

<template>
  <div></div>
</template>

<script setup>
import { ref, defineEmits } from 'vue'

const name = ref('天天鸭')
const emits = defineEmits(['addEvent'])
const handleSubmit = () => {
  emits('addEvent', name.value)
}
</script>

Parent component listens to the event:

<template>
  <child @addEvent="handle"></child>
</template>

<script setup>
import { ref } from 'vue'
import child from './child.vue'

const handle = value => {
  console.log(value) // '天天鸭'
}
</script>

3. Sibling Communication (mitt)

Vue 3 uses the mitt library as an event bus for sibling components.

npm install --save mitt

Global registration in main.js :

import mitt from "mitt"
const app = createApp(App)
app.config.globalProperties.$bus = new mitt()

Emitter component:

<script setup>
import mitt from 'mitt'
const emitter = mitt()
emitter.emit('custom-event', 'payload')
</script>

Listener component:

<script setup>
import mitt from 'mitt'
const emitter = mitt()
emitter.on('custom-event', payload => {
  console.log(payload)
})
</script>

4. $attrs (un‑declared props)

$attrs receives attributes that are not defined in props . Use useAttrs() to access them.

<template>
  <child :name="'天天鸭'" data="PC9527"/>
</template>

<script setup>
import child from './child.vue'
</script>
<template>
  <div>{{ props.name }}</div>
</template>

<script setup>
import { defineProps, useAttrs } from 'vue'
const props = defineProps({
  name: { type: String }
})
const myattrs = useAttrs()
console.log(myattrs) // { data: 'PC9527' }
</script>

5. Refs

Parent assigns a ref to the child component; the child must expose properties or methods with defineExpose .

<template>
  <child ref="myref"></child>
  <button @click="myClick">Click</button>
</template>

<script setup>
import child from "./child.vue"
import { ref } from "vue"
const myref = ref(null)
const myClick = () => {
  console.log(myref.value.name)
  myref.value.chileMethod()
}
</script>
<template>
  <div></div>
</template>

<script setup>
import { defineExpose, ref } from "vue"
const chileMethod = () => {
  console.log("我是方法")
}
const name = ref('天天鸭')
defineExpose({ name, chileMethod })
</script>

6. v‑model

v‑model is syntactic sugar for a prop plus an update: event.

<child v-model:title="title" />

<child :title="title" @update:title="title = $event" />

Parent usage:

<template>
  <child v-model:name="name" v-model:num="num"></child>
</template>

<script setup>
import child from "./child.vue"
import { ref } from "vue"
const name = ref("天天鸭")
const num = ref("2222")
</script>

Child triggers updates with emit :

<template>
  <button @click="myClick">Click</button>
</template>

<script setup>
import { defineEmits } from "vue"
const emit = defineEmits(["name", "num"])
const myClick = () => {
  emit("update:name", "改个新名字")
  emit("update:num", "换个新号码")
}
</script>

v‑model Extension (defineModel)

In Vue 3.4+, defineModel() provides a macro for two‑way binding without explicit props or emits.

<template>
  <button @click="handleClickCancel">Cancel</button>
</template>

<script setup>
import { defineModel } from 'vue'
const model = defineModel() // or defineModel({ type: Boolean })
const handleClickCancel = () => {
  model.value = false
}
</script>

7. provide / inject

Allows data to be passed through any number of component layers.

<template>
  <div></div>
</template>

<script setup>
import { ref, provide } from 'vue'
const name = ref('天天鸭')
provide('name', name.value)
</script>
<template>
  <div>{{ name }}</div>
</template>

<script setup>
import { inject } from 'vue'
const name = inject('name')
</script>

8. Router Parameters

Data can be passed via query , params (deprecated), or state .

// query
const query = { id: 9527, name: '天天鸭' }
router.push({ path: '/user', query })
const route = useRoute()
console.log(route.query)
// params (removed in Vue Router 4.1.4)
router.push({ name: 'test', params: { name: '天天鸭' } })
const route = useRoute()
console.log(route.params)
// state
const state = { name: '天天鸭' }
router.push({ path: '/user', state })
console.log(history?.state?.name)

9. Vuex

State management using Vuex (see related article for comparison with Pinia).

10. Pinia

State management using Pinia (see related article for comparison with Vuex).

11. Browser Storage

Use localStorage for persistent data and sessionStorage for temporary data.

// Store data
localStorage.setItem('key', 'value');
sessionStorage.setItem('key', 'value');

// Retrieve data
const valLocal = localStorage.getItem('key');
const valSession = sessionStorage.getItem('key');

// Remove data
localStorage.removeItem('key');
sessionStorage.removeItem('key');

// Clear all
localStorage.clear();
sessionStorage.clear();

12. Global Window Object

Directly attach properties to window , but this is not recommended for large projects.

window.duname = '天天鸭'
window.duObj = { test: '看看对象' }
console.log(window.duname)   // 天天鸭
console.log(window.duObj)   // {test: '看看对象'}

13. app.config.globalProperties

Vue 3 replaces Vue.prototype with app.config.globalProperties for adding global properties.

import { createApp } from 'vue'
const app = createApp(App)
app.config.globalProperties.msg = 'hello'
import { getCurrentInstance } from "vue";
const { proxy } = getCurrentInstance()
console.log(proxy.msg) // hello

Conclusion

This note serves as a personal reference for Vue 3 component communication. Feel free to point out any mistakes or omissions, and consider giving a like if you found it helpful.

frontendJavaScriptVueVue3Component Communication
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.