「基础梳理」Vue 3组件通信13种方法

发布于 2024-10-08  50 次阅读


Vue 3 中的 13 种组件通信方式详解

在 Vue 3 中,组件之间的通信是构建应用程序的关键。随着应用的复杂度增加,不同组件间的通信需求也变得更加多样化。在这篇博客中,我们将介绍 13 种常见的 Vue 3 组件通信方式,帮助开发者根据不同的场景选择最合适的解决方案。


1. 父组件向子组件传递数据 (Props)

通过 props,父组件可以轻松地将数据传递给子组件。这是最基础的通信方式。

父组件:

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

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

const name = ref('小明')
</script>

子组件:

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

<script setup>
import { defineProps } from 'vue'

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

2. 子组件向父组件传递数据 (Emit)

通过 emit 事件,子组件可以将数据发送回父组件。

子组件:

<template>
  <button @click="handleClick">点击我</button>
</template>

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

const message = ref('来自子组件的问候')
const emits = defineEmits(['greet'])

const handleClick = () => {
  emits('greet', message.value)
}
</script>

父组件:

<template>
  <child @greet="handleGreet"></child>
</template>

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

const handleGreet = (message) => {
  console.log(message) // 输出: "来自子组件的问候"
}
</script>

3. 兄弟组件通信 (Mitt)

Mitt 是一个轻量级的事件总线库,适用于兄弟组件之间的通信。

首先,安装 mitt

npm install --save mitt

然后在 main.js 中进行配置:

import { createApp } from 'vue'
import mitt from 'mitt'
import App from './App.vue'

const app = createApp(App)
app.config.globalProperties.$bus = mitt()

app.mount('#app')

发送事件的组件:

<script setup>
import { getCurrentInstance } from 'vue'

const { proxy } = getCurrentInstance()
const sendMessage = () => {
  proxy.$bus.emit('myEvent', '你好,兄弟')
}
</script>

接收事件的组件:

<script setup>
import { onMounted, getCurrentInstance } from 'vue'

const { proxy } = getCurrentInstance()

onMounted(() => {
  proxy.$bus.on('myEvent', (message) => {
    console.log(message) // 输出: "你好,兄弟"
  })
})
</script>

4. 透传 Attributes ($attrs)

$attrs 包含了父组件传递给子组件的所有非 props 属性。

父组件:

<template>
  <child name="小明" age="18" hobby="篮球"></child>
</template>

子组件:

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
console.log(attrs) // { age: "18", hobby: "篮球" }
</script>

5. 模板引用 (Refs)

通过 ref,父组件可以直接访问子组件的属性和方法。

父组件:

<template>
  <child ref="childRef"></child>
  <button @click="callChildMethod">调用子组件方法</button>
</template>

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

const childRef = ref(null)

const callChildMethod = () => {
  childRef.value.someMethod()
}
</script>

子组件:

<script setup>
import { defineExpose } from 'vue'

const someMethod = () => {
  console.log('子组件方法被调用了')
}

defineExpose({
  someMethod,
})
</script>

6. 双向绑定 (v-model)

v-model 提供了一种简洁的方式来实现父子组件之间的双向数据绑定。

父组件:

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

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

const name = ref('小明')
</script>

子组件:

<template>
  <input :value="name" @input="updateName" />
</template>

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

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

const updateName = (e) => {
  emit('update:name', e.target.value)
}
</script>

7. 依赖注入 (Provide/Inject)

provideinject 允许祖先组件向其所有子孙组件传递数据,而无需逐层传递。

祖先组件:

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

const themeColor = ref('blue')
provide('theme', themeColor)
</script>

子孙组件:

<script setup>
import { inject } from 'vue'

const theme = inject('theme')
console.log(theme.value) // 'blue'
</script>

8. 路由传参

通过 Vue Router,可以在不同路由之间传递参数。

import { useRouter } from 'vue-router'

const router = useRouter()
router.push({ path: '/user', query: { id: 123 } })

// 在目标组件中获取参数
import { useRoute } from 'vue-router'

const route = useRoute()
console.log(route.query.id) // 123

9. Vuex 状态管理

Vuex 是 Vue 的官方状态管理库,适用于大型应用。

// store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    count: 0,
  },
  mutations: {
    increment(state) {
      state.count++
    },
  },
})

// 在组件中使用
import { useStore } from 'vuex'

const store = useStore()
console.log(store.state.count)
store.commit('increment')

10. Pinia 状态管理

Pinia 是 Vue 的新一代状态管理库,提供更简单的 API 和更好的 TypeScript 支持。

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    },
  },
})

// 在组件中使用
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()
console.log(counter.count)
counter.increment()

11. 浏览器存储

通过 localStoragesessionStorage,可以在不同组件或页面之间共享数据。

// 存储数据
localStorage.setItem('user', JSON.stringify({ name: '小明', age: 18 }))

// 读取数据
const user = JSON.parse(localStorage.getItem('user'))

12. Window 对象

虽然不推荐,但在某些场景下,可以使用 window 对象来共享全局数据。

// 设置全局数据
window.globalData = { message: '全局消息' }

// 在任何地方使用
console.log(window.globalData.message)

13. 全局属性

通过 app.config.globalProperties,可以添加全局可用的属性或方法。

// main.js
const app = createApp(App)
app.config.globalProperties.$http = axios

// 在组件中使用
import { getCurrentInstance } from 'vue'

const { proxy } = getCurrentInstance()
proxy.$http.get('/api/data')

总结

在 Vue 3 中,组件通信有多种方式,从最基本的 propsemit,到更复杂的状态管理方案,如 Vuex 和 Pinia。这 13 种方法涵盖了几乎所有的组件通信场景。根据具体需求和应用规模,选择最合适的通信方式可以有效提高代码的可维护性和开发效率。

选择合适的通信方式不仅可以简化代码,还能增强组件的独立性和可复用性。希望这篇指南能帮助你更好地掌握 Vue 3 中的组件通信!


A Student on the way to full stack of Web3.