vue3組件通信方式
vue2組件通信方式:
- props:可以實現父子組件、子父組件、甚至兄弟組件通信
- 自定義事件:可以實現子父組件通信
- 全局事件總線$bus:可以實現任意組件通信
- pubsub:發布訂閱模式實現任意組件通信
- vuex:集中式狀態管理容器,實現任意組件通信
- ref:父組件獲取子組件實例VC,獲取子組件的響應式數據以及方法
- slot:插槽(默認插槽、具名插槽、作用域插槽)實現父子組件通信........
1.1props(父組件給子組件傳)
props可以實現父子組件通信,在vue3中我們可以通過defineProps獲取父組件傳遞的數據。且在組件內部不需要引入defineProps方法可以直接使用!
父組件給子組件傳遞數據
<Child info="我愛祖國" :money="money"></Child>
?子組件獲取父組件傳遞數據:方式1
let props = defineProps({info:{type:String,//接受的數據類型default:'默認參數',//接受默認數據},money:{type:Number,default:0
}})
子組件獲取父組件傳遞數據:方式2
let props = defineProps(["info",'money']);
子組件獲取到props數據就可以在模板中使用了,但是切記props是只讀的(只能讀取,不能修改)
1.2自定義事件(子組件給父組件傳)
在vue框架中事件分為兩種:一種是原生的DOM事件,另外一種自定義事件。
原生DOM事件可以讓用戶與網頁進行交互,比如click、dbclick、change、mouseenter、mouseleave....
自定義事件可以實現子組件給父組件傳遞數據
1.2.1原生DOM事件
代碼如下:
<pre @click="handler">我是祖國的老花骨朵</pre>
?當前代碼級給pre標簽綁定原生DOM事件點擊事件,默認會給事件回調注入event事件對象。當然點擊事件想注入多個參數可以按照下圖操作。但是切記注入的事件對象務必叫做$event.
<div @click="handler1(1,2,3,$event)">我要傳遞多個參數</div>
在vue3框架click、dbclick、change(這類原生DOM事件),不管是在標簽、自定義標簽上(組件標簽)都是原生DOM事件。
<!--vue2中卻不是這樣的,在vue2中組件標簽需要通過native修飾符才能變為原生DOM事件-->
1.2.2自定義事件
自定義事件可以實現子組件給父組件傳遞數據.在項目中是比較常用的。
比如在父組件內部給子組件(Event2)綁定一個自定義事件
<Event2 @xxx="handler3"></Event2>
?在Event2子組件內部觸發這個自定義事件defineEmits(["xxx"]);
<template><div><h1>我是子組件2</h1><button @click="handler">點擊我觸發xxx自定義事件</button></div>
</template><script setup lang="ts">
let $emit = defineEmits(["xxx"]);
const handler = () => {$emit("xxx", "法拉利", "茅臺");
};
</script>
<style scoped>
</style>
在script標簽內部,使用了defineEmits方法,此方法是vue3提供的方法,不需要引入直接使用。defineEmits方法執行,傳遞一個數組,數組元素即為將來組件需要觸發的自定義事件類型,此方執行會返回一個$emit方法用于觸發自定義事件。
當點擊按鈕的時候,事件回調內部調用$emit方法去觸發自定義事件,第一個參數為觸發事件類型,第二個、三個、N個參數即為傳遞給父組件的數據。
需要注意的是:代碼如下
<Event2 @xxx="handler3" @click="handler"></Event2>
正常說組件標簽書寫@click應該為原生DOM事件,但是如果子組件內部通過defineEmits定義就變為自定義事件了
let $emit = defineEmits(["xxx",'click']);
項目舉例
1.3全局事件總線
全局事件總線可以實現任意組件通信,在vue2中可以根據VM與VC關系推出全局事件總線。
但是在vue3中沒有Vue構造函數,也就沒有Vue.prototype.以及組合式API寫法沒有this,
那么在Vue3想實現全局事件的總線功能就有點不現實啦,如果想在Vue3中使用全局事件總線功能
可以使用插件mitt實現。
mitt:官網地址:mitt - npm
1.4v-model
v-model指令可是收集表單數據(數據雙向綁定),除此之外它也可以實現父子組件數據同步。
而v-model實指利用props[modelValue]與自定義事件[update:modelValue]實現的。
下方代碼:相當于給組件Child傳遞一個props(modelValue)與綁定一個自定義事件update:modelValue
實現父子組件數據同步
<Child v-model="msg"></Child>
在vue3中一個組件可以通過使用多個v-model,讓父子組件多個數據同步,下方代碼相當于給組件Child傳遞兩個props分別是pageNo與pageSize,以及綁定兩個自定義事件update:pageNo與update:pageSize實現父子數據同步
<Child v-model:pageNo="msg" v-model:pageSize="msg1"></Child>
?v-model解析
1.5useAttrs
在Vue3中可以利用useAttrs方法獲取組件的屬性與事件(包含:原生DOM事件或者自定義事件),次函數功能類似于Vue2框架中$attrs屬性與$listeners方法。
比如:在父組件內部使用一個子組件my-button
<my-button type="success" size="small" title='標題' @click="handler"></my-button>
子組件內部可以通過useAttrs方法獲取組件屬性與事件.因此你也發現了,它類似于props,可以接受父組件傳遞過來的屬性與屬性值。需要注意如果defineProps接受了某一個屬性,useAttrs方法返回的對象身上就沒有相應屬性與屬性值。
<script setup lang="ts">
import {useAttrs} from 'vue';
let $attrs = useAttrs();
</script>
1.6ref與$parent
ref,提及到ref可能會想到它可以獲取元素的DOM或者獲取子組件實例的VC。既然可以在父組件內部通過ref獲取子組件實例VC,那么子組件內部的方法與響應式數據父組件可以使用的。
比如:在父組件掛載完畢獲取組件實例
父組件內部代碼:
<template><div><h1>ref與$parent</h1><Son ref="son"></Son></div>
</template>
<script setup lang="ts">
import Son from "./Son.vue";
import { onMounted, ref } from "vue";
const son = ref();
onMounted(() => {console.log(son.value);
});
</script>
但是需要注意,如果想讓父組件獲取子組件的數據或者方法需要通過defineExpose對外暴露,因為vue3中組件內部的數據對外“關閉的”,外部不能訪問
<script setup lang="ts">
import { ref } from "vue";
//數據
let money = ref(1000);
//方法
const handler = ()=>{
}
defineExpose({money,handler
})
</script>
?項目舉例:
$parent可以獲取某一個組件的父組件實例VC,因此可以使用父組件內部的數據與方法。必須子組件內部擁有一個按鈕點擊時候獲取父組件實例,當然父組件的數據與方法需要通過defineExpose方法對外暴露
<button @click="handler($parent)">點擊我獲取父組件實例</button>
1.7provide與inject
provide[提供]
inject[注入]
vue3提供兩個方法provide與inject,可以實現隔輩組件傳遞參數
組件組件提供數據:
provide方法用于提供數據,此方法執需要傳遞兩個參數,分別提供數據的key與提供數據value
<script setup lang="ts">
import {provide} from 'vue'
provide('token','admin_token');
</script>
?后代組件可以通過inject方法獲取數據,通過key獲取存儲的數值
<script setup lang="ts">
import {inject} from 'vue'
let token = inject('token');
</script>
1.8pinia
pinia官網:Pinia 中文文檔
pinia也是集中式管理狀態容器,類似于vuex。但是核心概念沒有mutation、modules,使用方式參照官網?
1.9slot
插槽:默認插槽、具名插槽、作用域插槽可以實現父子組件通信.
默認插槽:
在子組件內部的模板中書寫slot全局組件標簽
<template><div><slot></slot></div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
在父組件內部提供結構:Todo即為子組件,在父組件內部使用的時候,在雙標簽內部書寫結構傳遞給子組件
注意開發項目的時候默認插槽一般只有一個
<Todo><h1>我是默認插槽填充的結構</h1>
</Todo>
具名插槽:
顧名思義,此插槽帶有名字在組件內部留多個指定名字的插槽。
下面是一個子組件內部,模板中留兩個插槽
<template><div><h1>todo</h1><slot name="a"></slot><slot name="b"></slot></div>
</template>
<script setup lang="ts">
</script><style scoped>
</style>
?父組件內部向指定的具名插槽傳遞結構。需要注意v-slot:可以替換為#
<template><div><h1>slot</h1><Todo><template v-slot:a> //可以用#a替換<div>填入組件A部分的結構</div></template><template v-slot:b>//可以用#b替換<div>填入組件B部分的結構</div></template></Todo></div>
</template><script setup lang="ts">
import Todo from "./Todo.vue";
</script>
<style scoped>
</style>
?
作用域插槽(可以傳數據的插槽)
作用域插槽:可以理解為,子組件數據由父組件提供,但是子組件內部決定不了自身結構與外觀(樣式)
子組件Todo代碼如下:
<template><div><h1>todo</h1><ul><!--組件內部遍歷數組--><li v-for="(item,index) in todos" :key="item.id"><!--作用域插槽將數據回傳給父組件--><slot :$row="item" :$index="index"></slot></li></ul></div>
</template>
<script setup lang="ts">
defineProps(['todos']);//接受父組件傳遞過來的數據
</script>
<style scoped>
</style>
父組件內部代碼如下:
<template><div><h1>slot</h1><Todo :todos="todos"><template v-slot="{$row,$index}"><!--父組件決定子組件的結構與外觀--><span :style="{color:$row.done?'green':'red'}">{{$row.title}}</span></template></Todo></div>
</template><script setup lang="ts">
import Todo from "./Todo.vue";
import { ref } from "vue";
//父組件內部數據
let todos = ref([{ id: 1, title: "吃飯", done: true },{ id: 2, title: "睡覺", done: false },{ id: 3, title: "打豆豆", done: true },
]);
</script>
<style scoped>
</style>