【乾貨】Vue3 元件通訊方式詳解

.NET快速开发框架發表於2024-06-26

前言

毫無疑問,元件通訊是Vue中非常重要的技術之一,它的出現能夠使我們非常方便的在不同元件之間進行資料的傳遞,以達到資料互動的效果。所以,學習元件通訊技術是非常有必要的,本文將總結Vue中關於元件通訊的八種方式,幫助大家在使用Vue的過程中更加得心應手!

如果文中有不對、疑惑的地方,歡迎在評論區留言指正!!

一、什麼是元件通訊

在開始之前我們需要明白什麼是元件通訊,元件通訊可以拆分為兩個部分:

  • 元件
  • 通訊

都知道元件是vue最強大的功能之一,vue中每一個.vue檔案我們都可以視之為一個元件,簡單來說元件就是對UI結構的複用。

通訊指的是傳送者透過某種媒體以某種格式來傳遞資訊到收信者以達到某個目的。廣義上,任何資訊的交通都是通訊。而元件間通訊即指元件(.vue)透過某種方式來傳遞資訊以達到某個目的,舉個例子我們在使用UI框架中的table元件,可能會往table元件中傳入某些資料,這個本質就形成了元件之間的通訊

二、為什麼要進行元件通訊

通訊的本質是資訊同步,共享。回到vue中,每個元件之間的都有獨自的作用域,元件間的資料是無法共享的但實際開發工作中我們常常需要讓元件之間共享資料,這也是元件通訊的目的要讓它們互相之間能進行通訊,這樣才能實現資料間的互動,完成某種功能的開發。

三、元件通訊的分類

元件間通訊的分類可以分成以下

  • 父子元件之間的通訊
  • 兄弟元件之間的通訊
  • 祖孫與後代元件之間的通訊
  • 非關係元件間之間的通訊

他們之間的關係如下圖:

元件

目前最常用是props/$emitvuex/pinia ,接下來是 provide/inject,其他不建議使用;
實際專案中,簡單父子元件傳遞採用props/$emit ,涉及全域性共享的資料一般採用 vuex/pinia 結合儲存物件localStorage/sessionStorage使用。

Vue3 元件通訊方式

四、Vue3 的八種元件通訊方式

  • props
  • $emit
  • expose / ref
  • $attrs
  • v-model
  • provide / inject
  • Vuex
  • mitt

五、Vue3 八種通訊方式用法講解

1. props

用 props 傳資料給子元件有兩種方法,如下

方法一,setup() 方法寫法

// Parent.vue 傳送
<child :msg1="msg1" :msg2="msg2"></child>
<script>
import child from "./child.vue"
import { ref, reactive } from "vue"
export default {
    data(){
        return {
            msg1:"這是傳級子元件的資訊1"
        }
    },
    setup(){
        // 建立一個響應式資料
        
        // 寫法一 適用於基礎型別  ref 還有其他用處,下面章節有介紹
        const msg2 = ref("這是傳級子元件的資訊2")
        
        // 寫法二 適用於複雜型別,如陣列、物件
        const msg2 = reactive(["這是傳級子元件的資訊2"])
        
        return {
            msg2
        }
    }
}
</script>

// Child.vue 接收
<script>
export default {
  props: ["msg1", "msg2"],// 如果這行不寫,下面就接收不到
  setup(props) {
    console.log(props) // { msg1:"這是傳給子元件的資訊1", msg2:"這是傳給子元件的資訊2" }
  },
}
</script>

方法二,setup 語法糖

// Parent.vue 傳送
<child :msg2="msg2"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg2 = ref("這是傳給子元件的資訊2")
    // 或者複雜型別
    const msg2 = reactive(["這是傳級子元件的資訊2"])
</script>

// Child.vue 接收
<script setup>
    // 不需要引入 直接使用
    // import { defineProps } from "vue"
    const props = defineProps({
        // 寫法一
        msg2: String
        // 寫法二
        msg2:{
            type:String,
            default:""
        }
    })
    console.log(props) // { msg2:"這是傳級子元件的資訊2" }
</script>

注意:

如果父元件是setup(),子元件setup 語法糖寫法的話,是接收不到父元件裡 data 的屬性,只能接收到父元件裡 setup 函式里傳的屬性。

如果父元件是setup 語法糖寫法,子元件setup()方法寫法,可以透過 props 接收到 data 和 setup 函式里的屬性,但是子元件要是在 setup 裡接收,同樣只能接收到父元件中 setup 函式里的屬性,接收不到 data 裡的屬性

官方也說了,既然用了 3,就不要寫 2 了,所以不推薦setup()方法寫法。下面的例子,一律只用語法糖的寫法。

2. $emit

// Child.vue 派發
<template>
    // 寫法一
    <button @click="emit('myClick')">按鈕</buttom>
    // 寫法二
    <button @click="handleClick">按鈕</buttom>
</template>
<script setup>
    
    // 方法一 適用於Vue3.2版本 不需要引入
    // import { defineEmits } from "vue"
    // 對應寫法一
    const emit = defineEmits(["myClick","myClick2"])
    // 對應寫法二
    const handleClick = ()=>{
        emit("myClick", "這是傳送給父元件的資訊")
    }
    
    // 方法二 不適用於 Vue3.2版本,該版本 useContext()已廢棄
    import { useContext } from "vue"
    const { emit } = useContext()
    const handleClick = ()=>{
        emit("myClick", "這是傳送給父元件的資訊")
    }
</script>

// Parent.vue 響應
<template>
    <child @myClick="onMyClick"></child>
</template>
<script setup>
    import child from "./child.vue"
    const onMyClick = (msg) => {
        console.log(msg) // 這是父元件收到的資訊
    }
</script>

3. expose / ref

父元件獲取子元件的屬性或者呼叫子元件方法。

// Child.vue
<script setup>
    // 方法一 不適用於Vue3.2版本,該版本 useContext()已廢棄
    import { useContext } from "vue"
    const ctx = useContext()
    // 對外暴露屬性方法等都可以
    ctx.expose({
        childName: "這是子元件的屬性",
        someMethod(){
            console.log("這是子元件的方法")
        }
    })
    
    // 方法二 適用於Vue3.2版本, 不需要引入
    // import { defineExpose } from "vue"
    defineExpose({
        childName: "這是子元件的屬性",
        someMethod(){
            console.log("這是子元件的方法")
        }
    })
</script>

// Parent.vue  注意 ref="comp"
<template>
    <child ref="comp"></child>
    <button @click="handlerClick">按鈕</button>
</template>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const comp = ref(null)
    const handlerClick = () => {
        console.log(comp.value.childName) // 獲取子元件對外暴露的屬性
        comp.value.someMethod() // 呼叫子元件對外暴露的方法
    }
</script>

4. attrs

attrs:包含父作用域裡除 class 和 style 除外的非 props 屬性集合

// Parent.vue 傳送
<child :msg1="msg1" :msg2="msg2" title="3333"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg1 = ref("1111")
    const msg2 = ref("2222")
</script>

// Child.vue 接收
<script setup>
    import { defineProps, useContext, useAttrs } from "vue"
    // 3.2版本不需要引入 defineProps,直接用
    const props = defineProps({
        msg1: String
    })
    // 方法一 不適用於 Vue3.2版本,該版本 useContext()已廢棄
    const ctx = useContext()
    // 如果沒有用 props 接收 msg1 的話就是 { msg1: "1111", msg2:"2222", title: "3333" }
    console.log(ctx.attrs) // { msg2:"2222", title: "3333" }
    
    // 方法二 適用於 Vue3.2版本
    const attrs = useAttrs()
    console.log(attrs) // { msg2:"2222", title: "3333" }
</script>

5. v-model

可以支援多個資料雙向繫結

// Parent.vue
<child v-model:key="key" v-model:value="value"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const key = ref("1111")
    const value = ref("2222")
</script>

// Child.vue
<template>
    <button @click="handlerClick">按鈕</button>
</template>
<script setup>
    
    // 方法一  不適用於 Vue3.2版本,該版本 useContext()已廢棄
    import { useContext } from "vue"
    const { emit } = useContext()
    
    // 方法二 適用於 Vue3.2版本,不需要引入
    // import { defineEmits } from "vue"
    const emit = defineEmits(["key","value"])
    
    // 用法
    const handlerClick = () => {
        emit("update:key", "新的key")
        emit("update:value", "新的value")
    }
</script>

6. provide / inject

provide / inject 為依賴注入

provide:可以讓我們指定想要提供給後代元件的資料或

inject:在任何後代元件中接收想要新增在這個元件上的資料,不管元件巢狀多深都可以直接拿來用

// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("name", "RDIF")
</script>

// Child.vue
<script setup>
    import { inject } from "vue"
    const name = inject("name")
    console.log(name) // RDIF
</script>

7. Vuex

// store/index.js
import { createStore } from "vuex"
export default createStore({
    state:{ count: 1 },
    getters:{
        getCount: state => state.count
    },
    mutations:{
        add(state){
            state.count++
        }
    }
})

// main.js
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store"
createApp(App).use(store).mount("#app")

// Page.vue
// 方法一 直接使用
<template>
    <div>{{ $store.state.count }}</div>
    <button @click="$store.commit('add')">按鈕</button>
</template>

// 方法二 獲取
<script setup>
    import { useStore, computed } from "vuex"
    const store = useStore()
    console.log(store.state.count) // 1

    const count = computed(()=>store.state.count) // 響應式,會隨著vuex資料改變而改變
    console.log(count) // 1 
</script>

8. mitt

Vue3 中沒有了 EventBus 跨元件通訊,但是現在有了一個替代的方案 mitt.js,原理還是 EventBus。

先安裝 npm i mitt -S

然後像以前封裝 bus 一樣,封裝一下

mitt.js
import mitt from 'mitt'
const mitt = mitt()
export default mitt

然後兩個元件之間通訊的使用

// 元件 A
<script setup>
import mitt from './mitt'
const handleClick = () => {
    mitt.emit('handleChange')
}
</script>

// 元件 B 
<script setup>
import mitt from './mitt'
import { onUnmounted } from 'vue'
const someMethed = () => { ... }
mitt.on('handleChange',someMethed)
onUnmounted(()=>{
    mitt.off('handleChange',someMethed)
})
</script>

六、參考資料

vue.js: https://cn.vuejs.org/

vuex是什麼:https://vuex.vuejs.org/zh/

工作中要使用Git,看這篇文章就夠了:http://www.guosisoft.com/article/detail/410508049313861

企業數字化轉型如何做?看過來:http://www.guosisoft.com/article/detail/408745545576517

Vue2.x 元件通訊方式:http://www.guosisoft.com/article/detail/411234710110277

【保姆級教程】Vue專案除錯技巧:http://www.guosisoft.com/article/detail/430312211521605

Vue 前端開發團隊風格指南(史上最全):http://www.guosisoft.com/article/detail/415491255230533

國思RDIF低程式碼快速開發平臺(支援vue2、vue3):http://www.guosisoft.com/article/detail/557095625134149

七、結語

如果本文對你有一點點幫助,點個贊支援一下吧,你的每一個【贊】都是我創作的最大動力 _

更多技術文章請往:

http://www.guosisoft.com/article

http://www.rdiframework.net/article

大家一起共同交流和進步呀!!

相關文章