父子元件通訊
父子元件通訊是指在前端開發的元件化架構中,父元件與子元件之間互相傳遞資料和觸發功能的一種機制。這種機制使得元件能夠保持獨立性的同時,也能協同工作,完成複雜的介面邏輯和資料互動。
透過一個用vue實現的demo為例:
在這個demo中:
需要使用
v-model
指令將文字框內的內容與變數進行雙向繫結,用於獲取文字框內的內容。透過一個數組儲存需要展示的內容,並且使用
v-for
遍歷陣列內的內容然後渲染。透過
@click='add'
為按鈕新增一個點選事件將文字框的輸入內容新增到陣列上。
//元件一 <div class="input-group"> <input type="text" v-model="item"> <button @click="add">新增</button> </div> //元件二(需要資料) <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div>
如果我們將元件一放在父元件內,將元件二放在子元件,那我們要怎麼將父元件中資料傳遞給子元件進行渲染呢?(元件二需要資料)
這個時候就需要進行父子元件的通訊。
父向子通訊
父元件向子元件傳遞資訊非常方便,透過子元件的屬性傳遞資料就好了。
元件一放在父元件,元件二放在子元件中。父元件將值v-bind
繫結傳給子元件,子元件使用defineProps
接受。
父元件:在<Child :toChild="toChild"></Child>
中用v-bind繫結toChild
變數傳遞給子元件。
<template> <div class="input-group"> <input type="text" v-model="item"> <button @click="add">新增</button> </div> //將資料傳遞給子元件 <Child :toChild="toChild"></Child> </template> <script setup> import Child from '@/components/child1.vue' import { ref } from "vue"; const item = ref('') const toChild = ref('') const add = () => { toChild.value = item.value item.value = '' } </script>
子元件:子元件用defineProps
接受父元件傳遞的資料並且透過設定一個watch
監聽器,當父元件透過toChild
屬性傳遞一個新的值,這個值就會被新增到子元件的列表中。
<template> <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div> </template> <script setup> import { defineProps } from 'vue' import { reactive, watch } from "vue"; const list = reactive(['html', 'css', 'javascript']) //接收父元件的資料 const props = defineProps({ toChild: '' }) //監聽資料的改變動態新增到陣列裡 watch(() => props.toChild, (newVal, oldVal) => { list.push(newVal) }) </script>
子向父通訊
子元件向父元件傳遞資料較為麻煩一點,常見的方法是透過自定義事件實現資料的傳遞。
將元件二放在父元件,將元件一放在子元件裡。
方法一
藉助釋出訂閱機制,子元件負責釋出事件攜帶引數,父元件訂閱該事件透過事件引數獲取子元件提供的值。
子元件:透過defineEmits
定義並建立一個add
事件,再透過觸發點選事件的處理函式用emits
釋出add
事件,並且攜帶資料。
<template> <div class="input-group"> <input type="text" v-model="item"> <button @click="add">新增</button> </div> </template> <script setup> import { ref } from "vue"; const item = ref('') const emits = defineEmits(['add'])//建立一個add事件 const add = () => { //將item變數給到父元件 emits('add', item.value)//釋出add事件 item.value = '' } </script>
父元件:透過<child @add="handle"></child>
給子元件繫結自定義的add
事件並且以handle
函式作為處理函式。當add
事件釋出時,就會觸發執行handle
函式並且透過handle
函式的引數接收子元件傳遞的資料。
<template> <!-- 訂閱add事件,子元件什麼時候釋出add事件,父元件就會執行handle函式,從而實現資料的共享 --> <child @add="handle"></child> <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div> </template> <script setup> import child from '@/components/child2.vue' import { reactive } from "vue"; const list = reactive(['html', 'css', 'javascript']) const handle = (value) => { list.push(value) } </script>
這種方法中子元件只需要釋出自定義事件並且攜帶資料,父元件只需要監聽自定義事件並且接受資料。
方法二
父元件藉助v-model
將資料繫結給子元件,子元件建立update:xxx
事件,並接收到該資料將修改後的資料emits(釋出)出來讓父元件接收修改後的資料。
父元件:在<child v-model:list="list"></child>
中,使用v-model:list
將父元件中的 list
資料(渲染陣列)傳遞給子元件並且進行繫結,當子元件釋出 update:list
事件後,父元件將接收到修改後的渲染陣列並且進行重新渲染。
<template> <div> <child v-model:list="list"></child> <div class="child"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </div> </div> </template> <script setup> import child from '@/components/child3.vue' import {reactive } from "vue"; const list = reactive(['html', 'css', 'javascript']) </script>
子元件:子元件透過defineProps
接收父元件傳送的list
,並且自定義一個Update:list
事件。當點選按鈕後觸發add
函式,將文字框資料放入list
中,然後釋出Update:list
事件並且攜帶著修改後的list
。隨著Update:list
事件的釋出,父元件就可以接收到修改後的list
。
<template> <div> <div class="input-group"> <input type="text" v-model="item"> <button @click="add">新增</button> </div> </div> </template> <script setup> import { ref } from "vue"; const item = ref('') const props = defineProps({ list: { type: Array, default: () => [] } }) const emits = defineEmits(['Update:list']) const add = () => { const arr = props.list arr.push(item.value) emits('Update:list', arr) item.value = '' } </script>
這種方法使父元件的操作變得簡潔,但將子元件的操作變得複雜了。父元件只需要和子元件進行雙向繫結資料就行了,子元件則需要接收資料再發布自定義的Update:xxx
事件將修改後的資料傳遞給父元件。
方法三
父元件透過ref
獲取子元件中defineExprose()
暴露出來的資料。
子元件:使用 defineExpose
暴露資料,子元件透過defineExpose({ list })
將更新後的渲染陣列暴露給父元件。
<template> <div class="input-group"> <input type="text" v-model="item"> <button @click="add">新增</button> </div> </template> <script setup> import { ref, reactive } from "vue"; const item = ref('') const list = reactive(['html', 'css', 'javascript']) const add = () => { list.push(item.value) item.value = '' } defineExpose({ list }) </script>
父元件:獲取子元件的資料,使用 ref
定義一個引用變數來引用子元件,並在適當的時機獲取子元件暴露的渲染陣列。將子元件暴露的渲染陣列作為v-for
的迴圈物件進行渲染。
<template> //獲取子元件的引用 <child ref="childRef"></child> <div class="child"> <ul> //childRef?.list表示只有在在子元件已經掛載完成後才能訪問子元件暴露的list資料 <li v-for="item in childRef?.list">{{ item }}</li> </ul> </div> </template> <script setup> import child from '@/components/child4.vue' import { ref, reactive, onMounted } from "vue"; //用ref定義一個引用變數 const childRef = ref(null); </script>
這種方法十分簡便,子元件只需要暴露資料給父元件,然後父元件引用子元件暴露的資料,但是需要注意生命週期,需要在元件掛載完成後父元件才能成功獲取子元件的資料。