欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue組件間的通信方式詳析

 更新時間:2022年09月29日 08:45:51   作者:BestAns???????  
本文介紹Vue組件間通信方式,Vue組件間通信一直是個重要的話題,雖然官方推出的Vuex狀態(tài)管理方案可以很好的解決組件之間的通信問題,但是在組件庫內(nèi)部使用Vuex往往會比較重,本文將系統(tǒng)的羅列出幾種不使用Vuex,比較實用的組件間的通信方式,希望能幫助到大家

前言

在Vue組件庫開發(fā)過程中,Vue組件之間的通信一直是一個重要的話題,雖然官方推出的 Vuex 狀態(tài)管理方案可以很好的解決組件之間的通信問題,但是在組件庫內(nèi)部使用 Vuex 往往會比較重,本文將系統(tǒng)的羅列出幾種不使用 Vuex,比較實用的組件間的通信方式,供大家參考。

組件之間通信的場景

在進(jìn)入我們今天的主題之前,我們先來總結(jié)下Vue組件之間通信的幾種場景,一般可以分為如下幾種場景:

  • 父子組件之間的通信
  • 兄弟組件之間的通信
  • 隔代組件之間的通信

父子組件之間的通信

父子組件之間的通信應(yīng)該是 Vue 組件通信中最簡單也最常見的一種了,概括為兩個部分:父組件通過prop向子組件傳遞數(shù)據(jù),子組件通過自定義事件向父組件傳遞數(shù)據(jù)。

父組件通過 prop 向子組件傳遞數(shù)據(jù)

Vue組件的數(shù)據(jù)流向都遵循單向數(shù)據(jù)流的原則,所有的 prop 都使得其父子 prop 之間形成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,但是反過來則不行。這樣會防止從子組件意外變更父級組件的狀態(tài),從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解。

額外的,每次父級組件發(fā)生變更時,子組件中所有的 prop 都將會刷新為最新的值。這意味著你不應(yīng)該在一個子組件內(nèi)部改變 prop。如果你這樣做了,Vue 會在瀏覽器的控制臺中發(fā)出警告。

父組件 ComponentA:

<template>
  <div>
    <component-b title="welcome"></component-b>
  </div>
</template>
<script>
import ComponentB from './ComponentB'

export default {
  name: 'ComponentA',
  components: {
    ComponentB
  }
}
</script>

子組件 ComponentB:

<template>
  <div>
    <div>{{title}}</div>
  </div>
</template>
<script>
export default {
  name: 'ComponentB',
  props: {
    title: {
      type: String,
    }
  }
} 
</script>

子組件通過自定義事件向父組件傳遞數(shù)據(jù)

在子組件中可以通過 $emit 向父組件發(fā)生一個事件,在父組件中通過 v-on/@ 進(jìn)行監(jiān)聽。

子組件 ComponentA:

<template>
  <div>
    <component-b :title="title" @title-change="titleChange"></component-b>
  </div>
</template>
<script>
import ComponentB from './ComponentB'

export default {
  name: 'ComponentA',
  components: {
    ComponentB
  },
  data: {
    title: 'Click me'
  },
  methods: {
    titleChange(newTitle) {
      this.title = newTitle
    } 
  }
}
</script>

子組件 ComponentB:

<template>
  <div>
    <div @click="handleClick">{{title}}</div>
  </div>
</template>
<script>
export default {
  name: 'ComponentB',
  props: {
    title: {
      type: String,
    }
  },
  methods: {
    handleClick() {
      this.$emit('title-change', 'New title !')
    }  
  }
} 
</script>

這個例子非常簡單,在子組件 ComponentB 里面通過 $emit 派發(fā)一個事件 title-change,在父組件 ComponentA 通過 @title-change 綁定的 titleChange 事件進(jìn)行監(jiān)聽,ComponentB 向 ComponentA 傳遞的數(shù)據(jù)在 titleChange 函數(shù)的傳參中可以獲取到。

兄弟組件之間的通信

狀態(tài)提升

寫過 React 的同學(xué)應(yīng)該對組件的 狀態(tài)提升 概念并不陌生,React 里面將組件按照職責(zé)的不同劃分為兩類:展示型組件(Presentational Component) 和 容器型組件(Container Component)。

展示型組件不關(guān)心組件使用的數(shù)據(jù)是如何獲取的,以及組件數(shù)據(jù)應(yīng)該如何修改,它只需要知道有了這些數(shù)據(jù)后,組件UI是什么樣子的即可。外部組件通過 props 傳遞給展示型組件所需的數(shù)據(jù)和修改這些數(shù)據(jù)的回調(diào)函數(shù),展示型組件只是它們的使用者。

容器型組件的職責(zé)是獲取數(shù)據(jù)以及這些數(shù)據(jù)的處理邏輯,并把數(shù)據(jù)和邏輯通過 props 提供給子組件使用。

因此,參考 React 組件中的 狀態(tài)提升 的概念,我們在兩個兄弟組件之上提供一個父組件,相當(dāng)于容器組件,負(fù)責(zé)處理數(shù)據(jù),兄弟組件通過 props 接收參數(shù)以及回調(diào)函數(shù),相當(dāng)于展示組件,來解決兄弟組件之間的通信問題。

ComponentA(兄弟組件A):

<template>
  <div>
    <div>{{title}}</div>
    <div @click="changeTitle">click me</div>
  </div>
</template>
<script>
export default {
  name: 'ComponentA',
  props: {
    title: {
      type: String
    },
    changeTitle: Function
  }
}
</script>

ComponentB(兄弟組件B):

<template>
  <div>
    <div>{{title}}</div>
    <div @click="changeTitle">click me</div>
  </div>
</template>
<script>
export default {
  name: 'ComponentB',
  props: {
    title: {
      type: String
    },
    changeTitle: Function
  }
}
</script>

ComponentC(容器組件C):

<template>
  <div>
    <component-a :title="titleA" :change-title="titleAChange"></component-a>
    <component-b :title="titleB" :change-title="titleBChange"></component-b>
  </div>
</template>
<script>
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'

export default {
  name: 'ComponentC',
  components: {
    ComponentA,
    ComponentB
  },
  data: {
    titleA: 'this is title A',
    titleB: 'this is title B'
  },
  methods: {
    titleAChange() {
      this.titleA = 'change title A'
    },
    titleBChange() {
      this.titleB = 'change title B'
    }
  }
}
</script>

可以看到,上述這種 "狀態(tài)提升" 的方式是比較繁瑣的,特別是兄弟組件的通信還要借助于父組件,組件復(fù)雜之后處理起來是相當(dāng)麻煩的。

隔代組件之間的通信

隔代組件之間的通信可以通過如下幾種方式實現(xiàn):

  • $attrs/$listeners
  • rovide/inject
  • 基于 $parent/$children 實現(xiàn)的 dispatch 和 broadcast

attrs/attrs/listeners

Vue 2.4.0 版本新增了 $attrs 和 $listeners 兩個方法。先看下官方對 $attrs 的介紹:

包含了父作用域中不作為 prop 被識別 (且獲取) 的 attribute 綁定(class 和 style 除外)。當(dāng)一個組件沒有聲明任何 prop 時,這里會包含所有父作用域的綁定 (class 和 style 除外),并且可以通過 v-bind="$attrs" 傳入內(nèi)部組件——在創(chuàng)建高級別的組件時非常有用。

看個例子:

組件A(ComponentA):

<template>
  <component-a name="Lin" age="24" sex="male"></component-a>
</template>
<script>
import ComponentB from '@/components/ComponentB.vue'

export default {
  name: 'App',
  components: {
    ComponentA
  }
}
</script>

組件B(ComponetB):

<template>
  <div>
    I am component B
    <component-c v-bind="$attrs"></component-c>
  </div>
</template>
<script>
import ComponentC from '@/components/ComponentC.vue'

export default {
  name: 'ComponentB',
  inheritAttrs: false,
  components: {
    ComponentC
  }
}
</script>

組件C(ComponetC):

<template>
  <div>
    I am component C
  </div>
</template>
<script>

export default {
  name: 'ComponentC',
  props: {
    name: {
      type: String
    }
  },
  mounted: function() {
    console.log('$attrs', this.$attrs)
  }
}
</script>

這里有三個組件,祖先組件(ComponentA)、父組件(ComponentB)和子組件(ComponentC)。這三個組件構(gòu)成了一個典型的子孫組件之間的關(guān)系。

ComponetA 給 ComponetB 傳遞了三個屬性 name、age 和 sex,ComponentB 通過 v-bind="$attrs" 將這三個屬性再透傳給 ComponentC, 最后在 ComponentC 中打印 $attrs 的值為:

{age: '24', sex: 'male'}

為什么我們一開始傳遞了三個屬性,最后只打印了兩個屬性 age 和 sex 呢?因為在 ComponentC 的props 中聲明了 name 屬性,$attrs 會自動排除掉在 props 中聲明的屬性,并將其他屬性以對象的形式輸出。

說白了就是一句話,$attrs 可以獲取父組件中綁定的非 Props 屬性

一般在使用的時候會同時和 inheritAttrs 屬性配合使用。

如果你不希望組件的根元素繼承 attribute,你可以在組件的選項中設(shè)置 inheritAttrs: false

在 ComponentB 添加了 inheritAttrs=false 屬性后,ComponentB 的dom結(jié)構(gòu)中可以看到是不會繼承父組件傳遞過來的屬性:

如果不加上 inheritAttrs=false 屬性,就會自動繼承父組件傳遞過來的屬性:

再看下 $listeners 的定義:

包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽器。它可以通過 v-on="$listeners" 傳入內(nèi)部組件——在創(chuàng)建更高層次的組件時非常有用。

$listeners也能把父組件中對子組件的事件監(jiān)聽全部拿到,這樣我們就能用一個v-on把這些來自于父組件的事件監(jiān)聽傳遞到下一級組件。

繼續(xù)改造 ComponentB 組件:

<template>
  <div>
    I am component B
    <component-c v-bind="$attrs" v-on="$listeners"></component-c>
  </div>
</template>
<script>
import ComponentC from '@/components/ComponentC.vue'

export default {
  name: 'ComponentB',
  inheritAttrs: false,
  components: {
    ComponentC
  }
}
</script>

這里利用 $attrs 和 $listeners 方法,可以將祖先組件(ComponentA) 中的屬性和事件透傳給孫組件(ComponentC),這樣就可以實現(xiàn)隔代組件之間的通信。

provide/inject

provide/inject 是 Vue 2.2.0 版本后新增的方法。

這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在其上下游關(guān)系成立的時間里始終生效。如果你熟悉 React,這與 React 的上下文特性很相似。

先看下簡單的用法:

父級組件:

export default {
  provide: {
    name: 'Lin'
  }
}

子組件:

export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // Lin
  }
}

上面的例子可以看到,父組件通過 privide 返回的對象里面的值,在子組件中通過 inject 注入之后可以直接訪問到。

但是需要注意的是,provide 和 inject 綁定并不是可響應(yīng)的,按照官方的說法,這是刻意為之的

也就是說父組件 provide 里面的name屬性值變化了,子組件中 this.name 獲取到的值不變。

如果想讓 provide 和 inject 變成可響應(yīng)的,有以下兩種方式:

  • provide 祖先組件的實例,然后在子孫組件中注入依賴,這樣就可以在子孫組件中直接修改祖先組件的實例的屬性,不過這種方法有個缺點(diǎn)就是這個實例上掛載很多沒有必要的東西比如props,methods
  • 使用 Vue 2.6 提供的 Vue.observable 方法優(yōu)化響應(yīng)式 provide

看一下第一種場景:

祖先組件組件(ComponentA):

export default {
  name: 'ComponentA',
  provide() {
    return {
      app: this
    }
  },
  data() {
    return {
       appInfo: {
         title: ''
       }
    }
  },
  methods: {
    fetchAppInfo() {
      this.appInfo = { title: 'Welcome to Vue world'}
    }
  }
}

我們把整個 ComponentA.vue 的實例 this 對外提供,命名為 app。接下來,任何組件只要通過 inject 注入 app 的話,都可以直接通過 this.app.xxx 來訪問 ComponentA.vue 的 datacomputed、methods 等內(nèi)容。

子組件(ComponentB):

<template>
  <div>
    {{ title }}
    <button @click="fetchInfo">獲取App信息</button>
  </div>
</template>
<script>
export default {
  name: 'ComponentB',
  inject: ['app'],
  computed: {
    title() {
      return this.app.appInfo.title
    }
  },
  methods: {
    fetchInfo() {
      this.app.fetchAppInfo()
    } 
  }
}
</script>

這樣,任何子組件,只要通過 inject 注入 app 后,就可以直接訪問祖先組件中的數(shù)據(jù)了,同時也可以調(diào)用祖先組件提供的方法修改祖先組件的數(shù)據(jù)并反應(yīng)到子組件上。

當(dāng)點(diǎn)擊子組件(ComponentB)的獲取App信息按鈕,會調(diào)用 this.app.fetchAppInfo 方法,也就是訪問祖先組件(ComponentA)實例上的 fetchAppInfo 方法,fetchAppInfo 會修改fetchAppInfo的值。同時子組件(ComponentB)中會監(jiān)聽 this.app.appInfo 的變化,并將變化后的title值顯示在組件上。

再看一下第二種場景,通過 Vue.observable 方法來實現(xiàn) provide 和 inject 綁定并可響應(yīng)。

基于上面的示例,改造祖先組件(ComponentA):

import Vue from 'vue'

const state = Vue.observable({ title: '' });
export default {
  name: 'ComponentA',
  provide() {
    return {
      state
    }
  }
}

使用 Vue.observable 定義一個可響應(yīng)的對象 state,并在 provide 中返回這個對象。

改造子組件(ComponentB):

<template>
  <div>
    {{ title }}
    <button @click="fetchInfo">獲取App信息</button>
  </div>
</template>
<script>
export default {
  name: 'ComponentInject',
  inject: ['state'],
  computed: {
    title() {
      return this.state.title
    }
  },
  methods: {
    fetchInfo() {
      this.state.title = 'Welcome to Vue world22'
    } 
  }
}
</script>

與之前的例子不同的是,這里我們直接修改了 this.state.title 的值,因為 state 被定義成了一個可響應(yīng)的數(shù)據(jù),所以 state.title 的值被修改后,視圖上的 title 也會立即響應(yīng)并更新,從這里看,其實很像 Vuex 的處理方式。

以上兩種方式對比可以發(fā)現(xiàn),第二種借助于 Vue.observable 方法實現(xiàn) provide 和 inject 的可響應(yīng)更加簡單高效,推薦大家使用這種方式。

基于 $parent/$children 實現(xiàn)的 dispatch 和 broadcast

先了解下 dispatch 和 broadcast 兩個概念:

  • dispatch: 派發(fā),指的是從一個組件內(nèi)部向上傳遞一個事件,并在組件內(nèi)部通過 $on 進(jìn)行監(jiān)聽
  • broadcast: 廣播,指的是從一個組件內(nèi)部向下傳遞一個事件,并在組件內(nèi)部通過 $on 進(jìn)行監(jiān)聽

在實現(xiàn) dispatch 和 broadcast 方法之前,先來看一下具體的使用方法。有 ComponentA.vue 和 ComponentB.vue 兩個組件,其中 ComponentB 是 ComponentA 的子組件,中間可能跨多級,在 ComponentA 中向 ComponentB 通信:

組件ComponentA:

<template>
  <button @click="handleClick">派發(fā)事件</button>
</template>
<script>
import Emitter from '../mixins/emitter.js';
export default {
  name: 'ComponentA',
  mixins: [Emitter],
  methods: {
    handleClick () {
      this.dispatch('ComponentB', 'on-message', 'Hello Vue.js')
    }
  }
}
</script>

組件ComponentB:

export default {
  name: 'ComponentB',
  created () {
    this.$on('on-message', this.showMessage)
  },
  methods: {
    showMessage (text) {
      console.log(text)
    }
  }
}

dispatch 的邏輯寫在 emitter.js 中,使用的時候通過 mixins 混入到組件中,這樣可以很好的將事件通信邏輯和組件進(jìn)行解耦。

dispatch 的方法有三個傳參,分別是:需要接受事件的組件的名字(全局唯一,用來精確查找組件)、事件名和事件傳遞的參數(shù)。

dispatch 的實現(xiàn)思路非常簡單,通過 $parent 獲取當(dāng)前父組件對象,如果組件的name和接受事件的name一致(dispatch方法的第一個參數(shù)),在父組件上調(diào)用 $emit 發(fā)射一個事件,這樣就會觸發(fā)目標(biāo)組件上 $on 定義的回調(diào)函數(shù),如果當(dāng)前組件的name和接受事件的name不一致,就遞歸地向上調(diào)用此邏輯。

dispath:

export default {
  methods: {
    dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root;
      let name = parent.$options.name;
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) {
          name = parent.$options.name
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    }
  }
}

broadcast邏輯和dispatch的邏輯差不多,只是一個是通過 $parent 向上查找,一個是通過 $children 向下查找,

export default {
  methods: {
    broadcast(componentName, eventName, params) {
      this.$children.forEach(child => {
        const name = child.$options.name
        if (name === componentName) {
          child.$emit.apply(child, [eventName].concat(params))
        } else {
          broadcast.apply(child, [componentName, eventName].concat([params]))
        }
      })
    }
  }
}

到此這篇關(guān)于Vue組件間的通信方式詳析的文章就介紹到這了,更多相關(guān)Vue組件通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue數(shù)據(jù)控制視圖源碼解析

    vue數(shù)據(jù)控制視圖源碼解析

    本篇內(nèi)容給大家詳細(xì)分析了關(guān)于vue數(shù)據(jù)控制視圖的源碼以及重點(diǎn)做了注釋,有興趣的朋友參考學(xué)習(xí)下。
    2018-03-03
  • elementUI中input回車觸發(fā)頁面刷新問題與解決方法

    elementUI中input回車觸發(fā)頁面刷新問題與解決方法

    這篇文章主要給大家介紹了關(guān)于elementUI中input回車觸發(fā)頁面刷新問題與解決方法,文中通過實例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用elementUI具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-07-07
  • Vue?組件之間傳值的主要方法

    Vue?組件之間傳值的主要方法

    父組件向子組件傳值,使用 props:可以通過在子組件上綁定 props,然后在父組件中通過 v-bind 綁定相應(yīng)的數(shù)據(jù)來傳遞數(shù)據(jù),這篇文章主要介紹了Vue?組件之間傳值的方法,需要的朋友可以參考下
    2023-12-12
  • element-ui中el-form-item內(nèi)的el-select該如何自適應(yīng)寬度

    element-ui中el-form-item內(nèi)的el-select該如何自適應(yīng)寬度

    自從用了element-ui,確實好用,該有的組件都有,但是組件間的樣式都固定好了,下面這篇文章主要給大家介紹了關(guān)于element-ui中el-form-item內(nèi)的el-select該如何自適應(yīng)寬度的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • 詳解vuex中mutations方法的使用與實現(xiàn)

    詳解vuex中mutations方法的使用與實現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了vuex中mutations方法的使用與實現(xiàn)的相關(guān)知識,文中的示例代碼簡潔易懂,具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-11-11
  • vue裁切預(yù)覽組件功能的實現(xiàn)步驟

    vue裁切預(yù)覽組件功能的實現(xiàn)步驟

    這篇文章主要介紹了vue裁切預(yù)覽組件功能的實現(xiàn)代碼,本文通過實例代碼相結(jié)合的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-05-05
  • vue中數(shù)據(jù)不響應(yīng)的問題及解決

    vue中數(shù)據(jù)不響應(yīng)的問題及解決

    這篇文章主要介紹了vue中數(shù)據(jù)不響應(yīng)的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • element中TimePicker時間選擇器禁用部分時間(顯示禁用到分鐘)

    element中TimePicker時間選擇器禁用部分時間(顯示禁用到分鐘)

    這篇文章主要介紹了element中TimePicker時間選擇器禁用部分時間(顯示禁用到分鐘),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • element-ui中樣式覆蓋問題的方法總結(jié)

    element-ui中樣式覆蓋問題的方法總結(jié)

    我們在使用element-ui的時候經(jīng)常會遇到需要修改組件默認(rèn)樣式,下面這篇文章主要給大家介紹了關(guān)于element-ui中樣式覆蓋問題的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • 把vue-router和express項目部署到服務(wù)器的方法

    把vue-router和express項目部署到服務(wù)器的方法

    下面小編就為大家分享一篇把vue-router和express項目部署到服務(wù)器的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02

最新評論