一文詳解Vue3中的14種組件通信方式
對于日常使用vue3
開發(fā)項目的前端小伙伴來說,組件通信方式可以說是必會的基本功,今天帶大家一起盤下vue3的通信方式。
我們按照組件的關系來劃分??偣舶?4中組件通信方式。
一:父子通信
1.1、父傳子:props
最最常用的通信方式是props
了,父組件通過props
方式將屬性傳遞給子組件,子組件接受props
并用于數據操作和頁面渲染。
注意:子組件不要直接修改父組件傳遞過來的props,保持自上而下單項數據流,這樣會讓數據的流向十分清晰,方便后續(xù)維護!
// Parent.vue <template> <Child :msg="msg"/> </template> <script setup> import { ref } from 'vue'; import Child from './components/Child.vue'; const msg = ref('hello world'); </script>
// Child.vue <template> <div> propsData:{{ msg }} </div> </template> <script setup> defineProps({ msg: String }) </script>
1.2、子傳父:defineEmits
通過defineEmits
可以讓子組件的值傳遞到父組件中。
其用法如下:
先在子組件中用defineEmits([...emitName])
定義一個或多個emit
,它的返回值是一個emits
函數,然后可以通過調用emits
函數向父組件發(fā)射時間,并攜帶參數。
// Child.vue <template> <div @click="onClick"> child </div> </template> <script setup> const emits = defineEmits(['update']); const onClick = () => { emits('update', 'child update'); } </script>
在父組件中通過@符 + 事件名
監(jiān)聽子組件發(fā)射 出來的事件,并接收其傳過來的值。
// Parent.vue <template> <div> Parent <Child @update="update"/> </div> </template> <script setup> import Child from './Child.vue'; const update = val => { console.log(val); // 當子組件點擊事件觸發(fā)后,這里會打印 child update } </script>
在vue2
的組件中還可以通過this.$on
、this.$emit
來監(jiān)聽、發(fā)射事件,以達到傳值的目的,但在vue3中已廢棄這種寫法。
1.3、$attrs
如果需要在子組件中接收的props
很多,如果在props
聲明比較繁瑣,所以vue給我們提供了一個優(yōu)雅的解決方案,即$attrs
,$attrs
指的是父組件傳遞給子組件的所有屬性中,剔除在props
中定義的那部分之后,剩下的就會放在$attrs
中。
舉個例子:
// Parent.vue <template> <div> Parent <Child :msg1="1" :msg2="2" /> </div> </template>
這里父組件給子組件傳遞了兩個屬性msg1
、msg2
。
// Child.vue <template> <div> child: {{ $attrs }} </div> </template> <script setup> defineProps({ msg1: String }) </script>
這里子組件使用了defineProps
定義了msg1
,則頁面中$attrs
的值為{ msg2: 2 }
。
還可以使用v-bind
將$attrs
的所有數據,以屬性的方式全部傳遞到子組件中,我們平常在封裝組件的時候,這個東西就能幫助我們實現(xiàn)組件的屬性透傳,十分好用。
<template> <Child v-bind="$attrs"/> </template>
注意:在vue3中$listeners
已廢棄,無法使用。
1.4、$ref + defineExpose
通過$ref
可以拿到組件的實例,defineExpose
可以顯式指定在 <script setup>
組件中要暴露出去的屬性,它兩一起配合使用,就能實現(xiàn)父子組件的通信。
其用法如下:
在子組件中通過defineExpose
暴露一個update
方法。
// Child.vue <script setup> defineExpose({ update(val) { console.log('父組件傳遞過來的值', val); } }) </script>
在父組件中通過ref
拿到組件實例并調用子組件暴露的update
方法。
// Parent.vue <template> <div> Parent <Child ref="childRef"/> </div> </template> <script setup> import Child from './Child.vue'; import { ref, onMounted } from 'vue'; const childRef = ref(null); onMounted(() => { childRef.value.update('hello') }) const update = () => {} </script>
1.5、$parent
$parent
代表當前組件的父組件實例,如果當前組件是頂層組件,則$parent
的值為null
。
我們可以通過$parent
拿到父組件的實例,自然就可以進行父子組件的交互了。一般也是和defineExpose
配合使用,和$ref + defineExpose
用法類似,這里就不多說了。
注意:$children
在vue3中已經廢棄,無法使用。
1.6、作用域插槽
通過作用域插槽可以實現(xiàn)子組件向父組件傳遞數據。
子組件代碼:
<template> <div> <slot :data="{ a:1, b: 2 }"/> </div> </template>
子組件可以在slot
標簽上傳遞數據給父組件。
父組件代碼:
<template> <Child> <template v-slot="slotProps"> {{ slotProps.data }} </template> </Child> </template> <script setup> import Child from './Child.vue'; </script>
父組件用v-slot
來接收數據,并渲染到頁面上。
1.7、v-model
v-model
可以在組件上使用以實現(xiàn)雙向綁定,vue
內部會幫你傳遞值和綁定事件,也是達到了父子組件通訊的效果。
從vue3.4
開始,還可以使用defineModel
便利宏,其用法如下:
子組件代碼:
// Child.vue <template> <div>model的值: {{ model }}</div> <button @click="handleClick">+1</button> </template> <script setup> const model = defineModel() function handleClick() { model.value++ } </script>
父組件代碼:
// Parent.vue <template> <Child1 v-model="modelValue"></Child1> Parent:{{ modelValue }} </template> <script setup> import Child from './Child.vue'; const modelValue = ref(0) </script>
defineModel
的返回值就是一個ref
,你可以隨意訪問和修改它,并且它會和父組件的v-model
綁定的值保持同步,也就是實現(xiàn)了雙向綁定。
二、兄弟組件
兩個兄弟關系組件進行通信,我們一般會借助第三方媒介。
2.1、mitt
mitt
相當于我們vue2
的事件總線$bus
,只是vue3
將其廢棄,所以我們借助mitt
實現(xiàn)類似$bus
的效果。
用法如下:
安裝mitt
npm install mitt
初始化mitt
// emitter.js import mitt from'mitt'; export default mitt();
兄弟組件1:
<script setup> import emitter from '@/utils/emitter' emitter.on('update', (val) => { console.log('update事件觸發(fā)', val) }) </script>
兄弟組件2:
<script setup> import emitter from '@/utils/emitter' setTimeout(() => { emitter.emit('update', 'hello') }, 1000) </script>
2.2、$parent
我們可以把狀態(tài)(即數據)定義在父組件中,兩個兄弟組件可以借助其共同的父組件共享同一份數據,間接實現(xiàn)通信。
2.3、vuex/pinia
vuex
是vue官方
提供的狀態(tài)管理工具,用它可以實現(xiàn)全局的狀態(tài)共享,自然也可以實現(xiàn)兄弟組件的通信了。當然也可以使用pinia
替代vuex
。
2.4、app.config.globalProperties
app.config.globalProperties
是一個全局的對象,在應用內所有組件實例都能訪問到,當組件屬性名和它發(fā)生同名沖突時,采取就近原則,以組件的為準,這個就相當于vue2
的Vue.prototype
。
三、跨層級通信
3.1、mitt
mitt
可以實現(xiàn)全局的通信,這個在上面介紹兄弟組件通信
的時候已經說過,這里不再多說了。
3.2、vuex/pinia
vuex
和pinia
都是全局的狀態(tài)管理工具,跨層級通信也不再話下。
3.3、provide/inject
provide/inject
是vue3
提供的可以跨層級通信的方式。
其用法如下:
在父組件/根組件
中定義provide
,提供數據。
// App.vue <script setup> import { ref, provide } from 'vue'; const name = ref('sam'); provide('name', name) </script>
在子組件/孫子組件
中使用inject
,注入數據。
// 后代組件 <script setup> import { inject } from 'vue'; const name = inject('name'); console.log('name', name.value); // 輸出name </script>
四、其它方式
4.1、瀏覽器本地存儲storage
html5
提供了一套storage API
,包括了localStorage
和sessionStorage
,它實現(xiàn)持久化存儲、緩存等功能,自然也可以用來組件間通信了。
// 組件A <script setup> sessionStorage.setItem('name', 'jack'); </script>
// 組件B <script setup> setTimeout(() => { console.log(sessionStorage.getItem('name')); //打印 jack }, 1000) </script>
這里我在組件A使用sessionStarge
設置了一個name
值,在組件B里面就能拿到了,只要保證獲取值在設置值之后執(zhí)行就行了。
4.2、全局window
對象(不推薦使用)
window
作為一個全局對象,當然也可以使用它來通信了,不過它既然誰都可以訪問到,就存在如下問題:
- 命令沖突問題;
- 難以追蹤數據修改,可維護性差;
- 掛在window上的數據難以銷毀,從而造成內存泄漏。
所以不推薦使用window
對象進行通信。
4.3、 ES6模塊化import/export
我們可以使用ES5的模塊化規(guī)范import/export
實現(xiàn)通信。
// a.js export let a = undefined; setTimeout(() => { a = 1; }, 1000)
// b.js import { a } from './a.js' setTimeout(() => { console.log(a); // 打印1 }, 2000)
由于ES module
采用的是符號綁定
,所以就算export
的值是一個基本數據類型的值
,后續(xù)修改了也能訪問到。
以上就是一文詳解Vue3中的14種組件通信方式的詳細內容,更多關于Vue3組件通信方式的資料請關注腳本之家其它相關文章!