前言
毫無疑問,組件通信是Vue中非常重要的技術之一,它的出現能夠使我們非常方便的在不同組件之間進行數據的傳遞,以達到數據交互的效果。所以,學習組件通信技術是非常有必要的,本文將總結Vue中關于組件通信的八種方式,幫助大家在使用Vue的過程中更加得心應手!
如果文中有不對、疑惑的地方,歡迎在評論區留言指正!!
一、什么是組件通信
在開始之前我們需要明白什么是組件通信,組件通信可以拆分為兩個部分:
- 組件
- 通信
都知道組件是vue
最強大的功能之一,vue
中每一個.vue
文件我們都可以視之為一個組件,簡單來說組件就是對UI結構的復用。
通信指的是發送者通過某種媒體以某種格式來傳遞信息到收信者以達到某個目的。廣義上,任何信息的交通都是通信。而組件間通信即指組件(.vue
)通過某種方式來傳遞信息以達到某個目的,舉個栗子我們在使用UI
框架中的table
組件,可能會往table
組件中傳入某些數據,這個本質就形成了組件之間的通信
二、為什么要進行組件通信
通信的本質是信息同步,共享。回到vue中,每個組件之間的都有獨自的作用域,組件間的數據是無法共享的但實際開發工作中我們常常需要讓組件之間共享數據,這也是組件通信的目的要讓它們互相之間能進行通訊,這樣才能實現數據間的交互,完成某種功能的開發。
三、組件通信的分類
組件間通信的分類可以分成以下
- 父子組件之間的通信
- 兄弟組件之間的通信
- 祖孫與后代組件之間的通信
- 非關系組件間之間的通信
他們之間的關系如下圖:
目前最常用是props/$emit
和 vuex/pinia
,接下來是 provide/inject
,其他不建議使用;
實際項目中,簡單父子組件傳遞采用props/$emit
,涉及全局共享的數據一般采用 vuex/pinia
結合存儲對象localStorage/sessionStorage
使用。
四、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) // 1const 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
【保姆級教程】Vue項目調試技巧:http://www.guosisoft.com/article/detail/430312211521605
Vue2.x 組件通信方式:http://www.guosisoft.com/article/detail/411234710110277
Vue 前端開發團隊風格指南(史上最全):http://www.guosisoft.com/article/detail/415491255230533
國思RDIF低代碼快速開發平臺(支持vue2、vue3):http://www.guosisoft.com/article/detail/557095625134149
七、結語
如果本文對你有一點點幫助,點個贊支持一下吧,你的每一個【贊】都是我創作的最大動力 _
更多技術文章請往:http://www.guosisoft.com/article,大家一起共同交流和進步呀
一路走來數個年頭,感謝RDIF框架的支持者與使用者,大家可以通過下面的地址了解詳情。
官方網站:http://www.guosisoft.com/ http://www.rdiframework.net/
特別說明,框架相關的技術文章請以官方網站為準,歡迎大家收藏!
國思RDIF低代碼快速開發框架由海南國思軟件科技有限公司專業團隊長期打造、一直在更新、一直在升級,請放心使用!
歡迎關注國思RDI低代碼快速開發框架官方公眾微信(微信號:guosisoft),及時了解最新動態。