日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

com组件的ref有时需要有时不需要?_Vue3组件通信总结

發(fā)布時(shí)間:2025/3/20 vue 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 com组件的ref有时需要有时不需要?_Vue3组件通信总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

我們知道vue3的Composition Api是它幾個(gè)最大亮點(diǎn)之一,所以下文都是在setup中演示代碼的實(shí)現(xiàn)。后面會(huì)以開發(fā)幾個(gè)簡單form組件為例子來演示。

基本操作

這里先簡單開發(fā)一個(gè)VInput的輸入框組件。組件就像一個(gè)函數(shù),主要就是處理輸入和輸出。Vue3在setup函數(shù)上提供了兩個(gè)參數(shù),一個(gè)props,一個(gè)是context下面的emit方法,分別來處理輸入和輸出。

props

現(xiàn)在VInput就是子組件,我需要它能夠接受父級(jí)傳遞一個(gè)值,讓它可以幫我做后續(xù)的邏輯處理在返回給父級(jí)。所以,這里需要最基本的一些父子通信方式v-bind,props。

父級(jí)組件中

// 通過v-bind將數(shù)據(jù)想子組件傳遞:value="valueRef" />

const valueRef = ref('')
復(fù)制代碼

VInput中

:value="value" type="text" />


復(fù)制代碼

emit

當(dāng)我們?cè)诮M件中接受參數(shù),進(jìn)行一些邏輯處理后,我們就需要將處理好的值,向外部進(jìn)行一個(gè)返回,外部同時(shí)需要實(shí)現(xiàn)一個(gè)事件函數(shù)去接受。此時(shí)我就可以使用emit方法

假設(shè)我們希望VInput組件返回給外部的是一個(gè)限制長度的字符串。此時(shí)外部就需要實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的事件函數(shù)去接收這個(gè)值,然后VInput內(nèi)部通emit執(zhí)行事件,將內(nèi)部的處理好的值當(dāng)做參數(shù)返回出去。

VInput

:value="value" type="text" @input="onInput" ref="inputRef" />


復(fù)制代碼

父級(jí)組件

// 通過v-on向子組件傳遞一個(gè)函數(shù),用戶接受返回值:value="valueRef" :maxLength="10" @onInput="onInput" />


復(fù)制代碼

對(duì)于這種input的組件的使用,我猜大家肯定都不想在父級(jí)組件這么麻煩的去接收和改變一個(gè)值,所以vue是提供了v-model來更快捷的實(shí)現(xiàn)輸入和輸出。

v-model

通過Vue3的文檔可以發(fā)現(xiàn),這個(gè)指令的用法發(fā)生了一定的變化。在之前,我們要想實(shí)現(xiàn)一個(gè)自定義的非表單組件的雙向綁定,需要通過xxxx.sync的這種語法來實(shí)現(xiàn),如今這個(gè)指令已經(jīng)被廢除了,而是統(tǒng)一使用v-model這個(gè)指令。

父級(jí)組件

新的v-model?還可以支持多個(gè)數(shù)據(jù)的雙向綁定。

v-model:value="valueRef" v-model:keyword="keywordRef" />
復(fù)制代碼

自定義的非表單組件

click="clickHandle">click

export default defineComponent({
name: 'VBtn',
props: {
value: String,
keyword: String
},
setup(props, { emit }) {
// 省略其他代碼

// 用戶點(diǎn)擊按鈕
const clickHandle = (e: any) => {
// 省略其他代碼

// 修改對(duì)應(yīng)的props的數(shù)據(jù)
emit('update:value', value)
emit('update:keyword', value + '123')
}

return {
// ...
}
}
})

復(fù)制代碼

以上就是在Vue3中一些基本通信方式的API的介紹。在Vue3中一般都是采用Composition Api的形式開發(fā),所以你會(huì)發(fā)現(xiàn)開發(fā)的時(shí)候不能在采用this.$xxx的方式去調(diào)用實(shí)例上的某個(gè)函數(shù)或者是屬性。那些this.$parent,this.$children,this.$on,this.$emit等等都不能在使用了。

那在Vue3中如何解決組件間那些通信的呢?咱們從簡單到復(fù)雜的場景,一個(gè)個(gè)來分析。

先來看一下,開發(fā)的三個(gè)form組件,組合起來的實(shí)際的用法是怎么樣的:

ref="validateFormRef1" :model="state" :rules="rules">label="用戶名" prop="keyword">placeholder="請(qǐng)輸入"requiredv-model:modelValue="state.keyword"
/>label="密碼" prop="password">placeholder="請(qǐng)輸入"requiredtype="password"v-model:modelValue="state.password"
/>class="btn btn-primary" @click="submit(0)">提交
復(fù)制代碼

所有組件的功能,是模仿Element UI去實(shí)現(xiàn)的。

父傳子

父組件向子組件傳遞一個(gè)數(shù)據(jù),可以用這兩種方式:

  • v-bind

  • refs獲取子組件內(nèi)部某個(gè)函數(shù),直接調(diào)用傳參(這里簡稱refs方式)

refs方式

關(guān)于v-bind咱們就不細(xì)說了,在基本操作章節(jié)已經(jīng)講過其對(duì)應(yīng)的使用方式了。這小節(jié)主要在中講Vue3如何通過ref獲取子組件實(shí)例并調(diào)用其身上的函數(shù)來對(duì)子組件進(jìn)行傳值。

子組件

// 渲染從父級(jí)接受到的值Son: {{ valueRef }}


復(fù)制代碼

父組件

sonRefclick="sendValue">send// 這里ref接受的字符串,要setup返回的ref類型的變量同名ref="sonRef" />


復(fù)制代碼

這里可以看一下流程圖:??其實(shí)這種方式跟Vue2中使用this.$refs,this.$children的方式很相似,都是通過拿到子組件實(shí)例,直接調(diào)用子組件身上的函數(shù)。方法千篇一律,不過在Vue3中沒有了this這個(gè)黑盒。

這里我們可以在控制臺(tái)看一下這個(gè)sonRef.value是一個(gè)怎樣的東西。

可以發(fā)現(xiàn),通過ref獲取到的子組件實(shí)例上面可以拿到setup返回的所有變量和方法,同時(shí)還可以拿到其他的一些內(nèi)部屬性。我們可以看一下官方文檔Vue 組合式 API的描述。

在 Virtual DOM patch 算法中,如果一個(gè) VNode 的 ref 對(duì)應(yīng)一個(gè)渲染上下文中的 ref,則該 VNode 對(duì)應(yīng)的元素或組件實(shí)例將被分配給該 ref。這是在 Virtual DOM 的 mount / patch 過程中執(zhí)行的,因此模板 ref 僅在渲染初始化后才能訪問。

ref方式總結(jié)

優(yōu)點(diǎn):

  • 父組件可以獲取快速向確定存在的子組件傳遞數(shù)據(jù)

  • 傳遞的參數(shù)不受限制,傳遞方式比較靈活

  • 缺點(diǎn):

  • ref獲取的子組件必須確定存在的(不確定存在的情況:如插槽上子組件,v-if控制的子組件)

  • 子組件還需要實(shí)現(xiàn)接受參數(shù)的方法

  • 父傳更深的后代

    一般往深度層級(jí)傳遞值,有這兩種方式:

    • provide / inject

    • vuex

    provide / inject

    一看到“深”這個(gè)字,大家肯定第一想到的就Vue2中的provide / inject選項(xiàng)。沒錯(cuò),這套邏輯在vue3中同樣適用,這兩個(gè)選項(xiàng)變成了兩個(gè)方法。

    provide允許我們向當(dāng)前組件的所有后代組件,傳遞一份數(shù)據(jù),所有后代組件能夠通過inject這個(gè)方法來決定是否接受這份數(shù)據(jù)。

    大致的示意圖如下:?

    實(shí)際應(yīng)用場景

    主要應(yīng)用的場景有兩中,一種深度傳遞一個(gè)參數(shù)或者一個(gè)函數(shù)的時(shí)候,另一種是給插槽上不確定性的組件傳參的時(shí)候。

    重點(diǎn)說一下給插槽上的組件傳參。先實(shí)現(xiàn)一個(gè)最外層的ValidateForm組件,它主要負(fù)責(zé)接受一整個(gè)表單數(shù)據(jù)和整個(gè)表單數(shù)據(jù)的校驗(yàn)規(guī)則。其內(nèi)部提供了一個(gè)插槽,用于放置一些不確定性的組件。還有一個(gè)ValidateFormItem組件可以接受一個(gè)字段名,通過這字段名準(zhǔn)確知道需要校驗(yàn)?zāi)膫€(gè)字段(tips:功能其實(shí)和element-ui類似)。

    組件化開發(fā),需要將參數(shù)和功能進(jìn)行解耦,所以我們這樣來設(shè)計(jì):

    • ValidateForm:model,rules,只管接受整份表單的數(shù)據(jù)和校驗(yàn)規(guī)則

    • ValidateFormItem:prop,只管接受字段名,只需知道自己需要驗(yàn)證哪一個(gè)字段

    ref="validateFormRef" :model="formData" :rules="rules">label="用戶名" prop="keyword">label="密碼" prop="password">
    復(fù)制代碼

    如果ValidateFormItem組件需要通過prop去效驗(yàn)?zāi)硞€(gè)字段,那它就需要拿到那份表單的數(shù)據(jù),通過formData[prop]去取到那個(gè)字段的值,那這份formData從哪里來呢?首先不可能每寫一個(gè)ValidateFormItem組件都傳遞一份。因?yàn)?#xff0c;實(shí)際開發(fā)中我們并不能確定在ValidateForm下要寫多少個(gè)ValidateFormItem組件,如果每寫一個(gè)都手動(dòng)傳遞一份表單的數(shù)據(jù),這些寫起來就會(huì)多了很多冗余的代碼而且也很麻煩。所以,就由ValidateForm這個(gè)組件獨(dú)立接受并分發(fā)下來。

    ValidateForm

    所以我們需要ValidateForm來向下分發(fā)數(shù)據(jù)。




    復(fù)制代碼

    ValidateFormItem

    ValidateFormItem接受上面?zhèn)鬟f的數(shù)據(jù)。


    復(fù)制代碼

    provide / inject總結(jié)

    在這篇文章Vue組件通信方式及其應(yīng)用場景總結(jié)中,大佬對(duì)其的優(yōu)缺點(diǎn)已經(jīng)總結(jié)很好了。這里提一下它的缺點(diǎn),就是不能解決兄弟組件的通信。

    vuex

    vuex一直以來是vue生態(tài)中一個(gè)解決不同層級(jí)組件數(shù)據(jù)共享的優(yōu)質(zhì)方案。不僅是在父傳子中可以適用,在子傳父,或者祖先傳后代,后代傳祖先,兄弟組件間都是一個(gè)非常好的方案。因?yàn)樗且粋€(gè)集中狀態(tài)管理模式。其本質(zhì)實(shí)現(xiàn)也是響應(yīng)式的。這里只簡單提一下Vue3中是如何使用的。

    創(chuàng)建一個(gè)store

    import { createStore } from 'vuex'

    export enum Mutarions {
    SET_COUNT = 'SET_COUNT'
    }

    export default createStore({
    state: {
    count: 231
    },
    getters: {
    count: state => state.count
    },
    mutations: {
    [Mutarions.SET_COUNT]: (state, num: number) => (state.count = num)
    }
    })
    復(fù)制代碼

    父組件

    fatherref="sonRef" />


    復(fù)制代碼

    子組件

    Son: {{ count }}


    復(fù)制代碼

    子傳父

    子級(jí)向父級(jí)傳遞數(shù)據(jù),可以有這三種方式:

    • v-on

    • refs方式

    • 事件中心

    refs方式

    通過ref的方式向父級(jí)傳遞一個(gè)數(shù)據(jù)是同樣適用的。具體思路:子組件內(nèi)部實(shí)現(xiàn)一個(gè)函數(shù),該函數(shù)可以返回一個(gè)值。父級(jí)組件通過ref取到子組件實(shí)例后調(diào)用該方法,得到需要的返回值。

    這里來看一下實(shí)際的應(yīng)用場景,我們希望ValidateForm組件去驗(yàn)證下面所有的表單項(xiàng),然后通過一個(gè)函數(shù)將組件內(nèi)部的一個(gè)驗(yàn)證狀態(tài)返回出去。

    父組件

    ref="validateFormRef" :model="formData" :rules="rules">label="用戶名" prop="keyword">label="密碼" prop="password">


    復(fù)制代碼

    ValidateForm




    復(fù)制代碼

    這里來看一下大致的流程圖:

    通過該種方法還可以拿到子組件內(nèi)部的數(shù)據(jù),這就跟閉包函數(shù)一樣的道理。

    事件中心

    這種通信方式為什么拿到這里來講呢?因?yàn)槲矣X接下的實(shí)際案例用上事件中心這種方式會(huì)非常的恰當(dāng)。在上一個(gè)小節(jié)中,我們留下來一個(gè)坑,那就是ValidateForm組件要去驗(yàn)證整個(gè)表單是否通過,就必須想辦法讓每個(gè)ValidateFormItem將內(nèi)部的校驗(yàn)結(jié)果返回給它。

    首先會(huì)遇到兩個(gè)問題

  • ValidateForm下面的組件是通過插槽去掛載的,所以無法通過ref的方式去拿到每個(gè)子表單項(xiàng)的實(shí)例,所以就沒辦法拿到每個(gè)ValidateFormItem的驗(yàn)證狀態(tài)了。

  • 上面的章節(jié)中有一個(gè)圖片,展示了通過ref拿到的組件實(shí)例。可以發(fā)現(xiàn),你可以找到$parent屬性,但是沒有$children屬性。這就很尷尬了,我們沒辦法像Vue2一樣在ValidateForm中通過$children拿到每個(gè)子組件的實(shí)例。

  • 解決思路

    既然沒有辦法拿到插槽上的組件實(shí)例,那咱們就繞開它,通過一個(gè)事件中心的方式來解決。思路是這樣的:

  • 在ValidateForm實(shí)例初始化的時(shí)候,去創(chuàng)建一個(gè)事件中心Emitter實(shí)例,它可以注冊(cè)一個(gè)事件,當(dāng)這個(gè)事件被執(zhí)行時(shí)可以接受一個(gè)函數(shù),并存在一個(gè)隊(duì)列中。

  • 將這個(gè)Emitter通過provide傳遞給后代,保證這個(gè)事件中心在不同的ValidateForm組件中都是獨(dú)立的。換句話說,就是如果寫了多個(gè)ValidateForm,他們的事件中心不會(huì)相互干擾。

  • 在ValidateFormItem中使用inject接收自己所在表單域的Emitter,在掛載的時(shí)候,執(zhí)行Emitter上的事件,將自己的內(nèi)部的validate函數(shù),傳遞發(fā)送給ValidateForm,并由其將方法緩存在隊(duì)列中。

  • ValidateForm執(zhí)行校驗(yàn)的時(shí)候,就可以執(zhí)行隊(duì)列中的所有校驗(yàn)函數(shù),并得出校驗(yàn)結(jié)果。

  • 具體代碼實(shí)現(xiàn):

    先來實(shí)現(xiàn)一個(gè)Emitter事件中心的類

    import { EmitterHandles } from '@/type/utils'

    export class Emitter {
    // 存放事件函數(shù)
    private events: EmitterHandles = {}

    // 用于注冊(cè)事件
    on(eventName: string, eventHandle: Function) {
    this.events[eventName] = eventHandle
    }

    // 刪除事件
    off(eventName: string) {
    if (this.events[eventName]) {
    delete this.events[eventName]
    }
    }

    // 觸發(fā)事件
    emit(eventName: string, ...rest: any[]) {
    if (this.events[eventName]) {
    this.events[eventName](...rest)
    }
    }
    }
    復(fù)制代碼

    當(dāng)事件中心實(shí)現(xiàn)好了,這里來完善一下ValidateForm的代碼


    復(fù)制代碼

    ok,現(xiàn)在實(shí)現(xiàn)了validateForm的邏輯,我們?cè)賮韺懸幌聉alidateFormItem的邏輯

    class="form-group">v-if="label" class=" col-form-label">{{ label }}v-if="error.isError" class="invalid-feedback">
    {{ error.errorMessage }}


    復(fù)制代碼

    為了更詳細(xì)的理解上面的過程,這里來畫一個(gè)示意圖:

  • 注冊(cè)事件,分發(fā)事件中心

  • 執(zhí)行事件,發(fā)送驗(yàn)證函數(shù)

  • 整個(gè)過程的總結(jié)就是,頂層組件創(chuàng)建和分發(fā)事件中心,并注冊(cè)事件監(jiān)聽函數(shù)。后代組件執(zhí)行該事件然后發(fā)送信息,頂層組件回收信息。

    Tips

    這里再提一點(diǎn),在使用Emitter這個(gè)事件中心的時(shí)候,是在ValidateForm的setup中去創(chuàng)建并且去下發(fā)的,并不是使用一個(gè)全局的事件中心。就像大佬的這篇文章Vue組件通信方式及其應(yīng)用場景總結(jié)中總結(jié)到的,事件總線的形式是有一個(gè)致命缺點(diǎn)的,如果一個(gè)頁面上有多個(gè)公共組件,我們只要向其中的一個(gè)傳遞數(shù)據(jù),但是每個(gè)公共組件都綁定了數(shù)據(jù)接受的方法,那就會(huì)出現(xiàn)混亂的情況。但是,我們的事件總線不是一個(gè)全局的,而是單個(gè)作用域里面的一個(gè)事件中心。

    因?yàn)槭录行氖窃诋?dāng)前組件內(nèi)部創(chuàng)建,并使用provide向下發(fā)布的,這樣就只有當(dāng)前組件的后代才能使用這個(gè)事件中心。所以,就算一個(gè)面上寫了多個(gè)ValidateForm,他們的校驗(yàn)都是獨(dú)立的。

    ref="validateFormRef1" :model="formData1" :rules="rules">label="用戶名" prop="keyword">label="密碼" prop="password">ref="validateFormRef2" :model="formData2" :rules="rules">label="用戶名" prop="keyword">label="密碼" prop="password">
    復(fù)制代碼

    示意圖:

    事件中心總結(jié)

    優(yōu)點(diǎn):

  • 可以解決Vue3不能使用this.$children的問題

  • 可以靈活使用,不受組件層級(jí)的限制

  • 這種通信方式不受框架的限制

  • 缺點(diǎn):

  • 需要控制好事件中心的作用范圍

  • 需要控制好事件名的規(guī)范

  • 事件中心進(jìn)階

    因?yàn)樵赩ue3的Composition API中,vue的功能api更加的顆粒化。我們可以對(duì)事件中心進(jìn)行一個(gè)自定義需求的改造。

    可以通過引入reactive, ref幫助我們的事件中心內(nèi)部維護(hù)一個(gè)響應(yīng)式的數(shù)據(jù),可以實(shí)現(xiàn)當(dāng)事件中心進(jìn)行一定通信行為時(shí),去更新對(duì)應(yīng)的視圖。還可以引入computed實(shí)現(xiàn)計(jì)算屬性的功能。

    import { reactive, ref, computed } from 'vue'

    export class Emitter {
    // 響應(yīng)式的數(shù)據(jù)中心
    private state = reactive({})
    private events: EmitterHandles = ref({})

    // 記錄當(dāng)前事件中心 事件的數(shù)量
    private eventLength = computed(() => Object.keys(events.value).length)

    // 省略部分代碼
    }

    復(fù)制代碼

    加入watch,watchEffect實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽做出一定邏輯行為的功能。我認(rèn)為Composition API和React Hooks Api都是非常強(qiáng)大,因?yàn)樗鼈冊(cè)试S我們將功能函數(shù)當(dāng)成積木一樣去任意組裝成我們希望得到的應(yīng)用程序。

    深層后代向頂層通信,兄弟通信

    我覺得其實(shí)其他的場景,其通信方式基本都差不多了,所謂千篇一律。后代向祖先傳值,或者兄弟組件傳值,都可以使用vuex或者是事件中心的方式。兄弟層級(jí),或者相鄰層級(jí)的,就可以使用ref,$parent等方式。

    總結(jié)

    以上是生活随笔為你收集整理的com组件的ref有时需要有时不需要?_Vue3组件通信总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。