vue中8種組件通信方式, 值得收藏!

来源:https://www.cnblogs.com/coderhf/archive/2020/06/29/13210310.html

vue是數據驅動視圖更新的框架, 所以對於vue來說組件間的數據通信非常重要,那麼組件之間如何進行數據通信的呢? 首先我們需要知道在vue中組件之間存在什麼樣的關係, 才更容易理解他們的通信方式, 就好像過年回家,坐著一屋子的陌生人,相互之間怎麼稱呼,這時就需要先知道自己和他們之間是什麼樣的關係。 ...


vue是數據驅動視圖更新的框架, 所以對於vue來說組件間的數據通信非常重要,那麼組件之間如何進行數據通信的呢? 首先我們需要知道在vue中組件之間存在什麼樣的關係, 才更容易理解他們的通信方式, 就好像過年回家,坐著一屋子的陌生人,相互之間怎麼稱呼,這時就需要先知道自己和他們之間是什麼樣的關係。 vue組件中關係說明:

image

 

如上圖所示, A與B、A與C、B與D、C與E組件之間是父子關係; B與C之間是兄弟關係;A與D、A與E之間是隔代關係; D與E是堂兄關係(非直系親屬) 針對以上關係我們歸類為:

  • 父子組件之間通信

  • 非父子組件之間通信(兄弟組件、隔代關係組件等)

本文會介紹組件間通信的8種方式如下圖目錄所示:並介紹在不同的場景下如何選擇有效方式實現的組件間通信方式,希望可以幫助小伙伴們更好理解組件間的通信。

img

 

一、props / $emit

父組件通過props的方式向子組件傳遞數據,而通過$emit 子組件可以向父組件通信。

1. 父組件向子組件傳值

下麵通過一個例子說明父組件如何向子組件傳遞數據:在子組件article.vue中如何獲取父組件section.vue中的數據articles:['紅樓夢', '西游記','三國演義']

 // section父組件
 <template>
   <div class="section">
     <com-article :articles="articleList"></com-article>
   </div>
 </template>
 
 <script>
 import comArticle from './test/article.vue'
 export default {
   name: 'HelloWorld',
   components: { comArticle },
   data() {
     return {
       articleList: ['紅樓夢', '西游記', '三國演義']
    }
  }
 }
 </script>
 
 複製代碼
 // 子組件 article.vue
 <template>
   <div>
     <span v-for="(item, index) in articles" :key="index">{{item}}</span>
   </div>
 </template>
 
 <script>
 export default {
   props: ['articles']
 }
 </script>

總結: prop 只可以從上一級組件傳遞到下一級組件(父子組件),即所謂的單向數據流。而且 prop 只讀,不可被修改,所有修改都會失效並警告。

2. 子組件向父組件傳值

對於$emit 我自己的理解是這樣的: $emit綁定一個自定義事件, 當這個語句被執行時, 就會將參數arg傳遞給父組件,父組件通過v-on監聽並接收參數。 通過一個例子,說明子組件如何向父組件傳遞數據。 在上個例子的基礎上, 點擊頁面渲染出來的ariticleitem, 父組件中顯示在數組中的下標

 // 父組件中
 <template>
   <div class="section">
     <com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
     <p>{{currentIndex}}</p>
   </div>
 </template>
 
 <script>
 import comArticle from './test/article.vue'
 export default {
   name: 'HelloWorld',
   components: { comArticle },
   data() {
     return {
       currentIndex: -1,
       articleList: ['紅樓夢', '西游記', '三國演義']
    }
  },
   methods: {
     onEmitIndex(idx) {
       this.currentIndex = idx
    }
  }
 }
 </script>
 <template>
   <div>
     <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
   </div>
 </template>
 
 <script>
 export default {
   props: ['articles'],
   methods: {
     emitIndex(index) {
       this.$emit('onEmitIndex', index)
    }
  }
 }
 </script>

二、 $children / $parent

 

image

上面這張圖片是vue``$parent``$children``data

 

使用方法

 // 父組件中
 <template>
   <div class="hello_world">
     <div>{{msg}}</div>
     <com-a></com-a>
     <button @click="changeA">點擊改變子組件值</button>
   </div>
 </template>
 
 <script>
 import ComA from './test/comA.vue'
 export default {
   name: 'HelloWorld',
   components: { ComA },
   data() {
     return {
       msg: 'Welcome'
    }
  },
 
   methods: {
     changeA() {
       // 獲取到子組件A
       this.$children[0].messageA = 'this is new value'
    }
  }
 }
 </script>
 複製代碼
 // 子組件中
 <template>
   <div class="com_a">
     <span>{{messageA}}</span>
     <p>獲取父組件的值為: {{parentVal}}</p>
   </div>
 </template>
 
 <script>
 export default {
   data() {
     return {
       messageA: 'this is old'
    }
  },
   computed:{
     parentVal(){
       return this.$parent.msg;
    }
  }
 }
 </script>

要註意邊界情況,如在#app上拿$parent得到的是new Vue()的實例,在這實例上再拿$parent得到的是undefined,而在最底層的子組件拿$children是個空數組。也要註意得到$parent$children的值不一樣,$children 的值是數組,而$parent是個對象

總結

上面兩種方式用於父子組件之間的通信, 而使用props進行父子組件通信更加普遍; 二者皆不能用於非父子組件之間的通信。

三、provide/ inject

概念:

provide/ injectvue2.2.0新增的api, 簡單來說就是父組件中通過provide來提供變數, 然後再子組件中通過inject來註入變數。

註意: 這裡不論子組件嵌套有多深, 只要調用了inject 那麼就可以註入provide中的數據,而不局限於只能從當前父組件的props屬性中回去數據

舉例驗證

接下來就用一個例子來驗證上面的描述: 假設有三個組件: A.vue、B.vue、C.vue 其中 C是B的子組件,B是A的子組件

 // A.vue
 
 <template>
   <div>
  <comB></comB>
   </div>
 </template>
 
 <script>
   import comB from '../components/test/comB.vue'
   export default {
     name: "A",
     provide: {
       for: "demo"
    },
     components:{
       comB
    }
  }
 </script>
 複製代碼
 // B.vue
 
 <template>
   <div>
    {{demo}}
     <comC></comC>
   </div>
 </template>
 
 <script>
   import comC from '../components/test/comC.vue'
   export default {
     name: "B",
     inject: ['for'],
     data() {
       return {
         demo: this.for
      }
    },
     components: {
       comC
    }
  }
 </script>
 複製代碼
 // C.vue
 <template>
   <div>
    {{demo}}
   </div>
 </template>
 
 <script>
   export default {
     name: "C",
     inject: ['for'],
     data() {
       return {
         demo: this.for
      }
    }
  }
 </script>

四、ref / refs

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子組件上,引用就指向組件實例,可以通過實例直接調用組件的方法或訪問數據, 我們看一個ref 來訪問組件的例子:

 // 子組件 A.vue
 
 export default {
   data () {
     return {
       name: 'Vue.js'
    }
  },
   methods: {
     sayHello () {
       console.log('hello')
    }
  }
 }
 複製代碼
 // 父組件 app.vue
 
 <template>
   <component-a ref="comA"></component-a>
 </template>
 <script>
   export default {
     mounted () {
       const comA = this.$refs.comA;
       console.log(comA.name);  // Vue.js
       comA.sayHello();  // hello
    }
  }
 </script>

五、eventBus

eventBus 又稱為事件匯流排,在vue中可以使用它來作為溝通橋梁的概念, 就像是所有組件共用相同的事件中心,可以向該中心註冊發送事件或接收事件, 所以組件都可以通知其他組件。

eventBus也有不方便之處, 當項目較大,就容易造成難以維護的災難

在Vue的項目中怎麼使用eventBus來實現組件之間的數據通信呢?具體通過下麵幾個步驟

1. 初始化

首先需要創建一個事件匯流排並將其導出, 以便其他模塊可以使用或者監聽它.

 // event-bus.js
 
 import Vue from 'vue'
 export const EventBus = new Vue()

2. 發送事件

假設你有兩個組件: additionNumshowNum, 這兩個組件可以是兄弟組件也可以是父子組件;這裡我們以兄弟組件為例:

 <template>
   <div>
     <show-num-com></show-num-com>
     <addition-num-com></addition-num-com>
   </div>
 </template>
 
 <script>
 import showNumCom from './showNum.vue'
 import additionNumCom from './additionNum.vue'
 export default {
   components: { showNumCom, additionNumCom }
 }
 </script>
 
 複製代碼
 // addtionNum.vue 中發送事件
 
 <template>
   <div>
     <button @click="additionHandle">+加法器</button>    
   </div>
 </template>
 
 <script>
 import {EventBus} from './event-bus.js'
 console.log(EventBus)
 export default {
   data(){
     return{
       num:1
    }
  },
 
   methods:{
     additionHandle(){
       EventBus.$emit('addition', {
         num:this.num++
      })
    }
  }
 }
 </script>

3. 接收事件

 // showNum.vue 中接收事件
 
 <template>
   <div>計算和: {{count}}</div>
 </template>
 
 <script>
 import { EventBus } from './event-bus.js'
 export default {
   data() {
     return {
       count: 0
    }
  },
 
   mounted() {
     EventBus.$on('addition', param => {
       this.count = this.count + param.num;
    })
  }
 }
 </script>

這樣就實現了在組件addtionNum.vue中點擊相加按鈕, 在showNum.vue中利用傳遞來的 num 展示求和的結果.

4. 移除事件監聽者

如果想移除事件的監聽, 可以像下麵這樣操作:

 import { eventBus } from 'event-bus.js'
 EventBus.$off('addition', {})

六、Vuex

1. Vuex介紹

Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化. Vuex 解決了多個視圖依賴於同一狀態來自不同視圖的行為需要變更同一狀態的問題,將開發者的精力聚焦於數據的更新而不是數據在組件之間的傳遞上

2. Vuex各個模塊

  1. state:用於數據的存儲,是store中的唯一數據源

  2. getters:如vue中的計算屬性一樣,基於state數據的二次包裝,常用於數據的篩選和多個數據的相關性計算

  3. mutations:類似函數,改變state數據的唯一途徑,且不能用於處理非同步事件

  4. actions:類似於mutation,用於提交mutation來改變狀態,而不直接變更狀態,可以包含任意非同步操作

  5. modules:類似於命名空間,用於項目中將各個模塊的狀態分開定義和操作,便於維護

3. Vuex實例應用

 // 父組件
 
 <template>
   <div id="app">
     <ChildA/>
     <ChildB/>
   </div>
 </template>
 
 <script>
   import ChildA from './components/ChildA' // 導入A組件
   import ChildB from './components/ChildB' // 導入B組件
 
   export default {
     name: 'App',
     components: {ChildA, ChildB} // 註冊A、B組件
  }
 </script>
 複製代碼
 // 子組件childA
 
 <template>
   <div id="childA">
     <h1>我是A組件</h1>
     <button @click="transform">點我讓B組件接收到數據</button>
     <p>因為你點了B,所以我的信息發生了變化:{{BMessage}}</p>
   </div>
 </template>
 
 <script>
   export default {
     data() {
       return {
         AMessage: 'Hello,B組件,我是A組件'
      }
    },
     computed: {
       BMessage() {
         // 這裡存儲從store里獲取的B組件的數據
         return this.$store.state.BMsg
      }
    },
     methods: {
       transform() {
         // 觸發receiveAMsg,將A組件的數據存放到store里去
         this.$store.commit('receiveAMsg', {
           AMsg: this.AMessage
        })
      }
    }
  }
 </script>
 複製代碼
 // 子組件 childB
 
 <template>
   <div id="childB">
     <h1>我是B組件</h1>
     <button @click="transform">點我讓A組件接收到數據</button>
     <p>因為你點了A,所以我的信息發生了變化:{{AMessage}}</p>
   </div>
 </template>
 
 <script>
   export default {
     data() {
       return {
         BMessage: 'Hello,A組件,我是B組件'
      }
    },
     computed: {
       AMessage() {
         // 這裡存儲從store里獲取的A組件的數據
         return this.$store.state.AMsg
      }
    },
     methods: {
       transform() {
         // 觸發receiveBMsg,將B組件的數據存放到store里去
         this.$store.commit('receiveBMsg', {
           BMsg: this.BMessage
        })
      }
    }
  }
 </script>

vuex的store,js

 import Vue from 'vue'
 import Vuex from 'vuex'
 Vue.use(Vuex)
 const state = {
   // 初始化A和B組件的數據,等待獲取
   AMsg: '',
   BMsg: ''
 }
 
 const mutations = {
   receiveAMsg(state, payload) {
     // 將A組件的數據存放於state
     state.AMsg = payload.AMsg
  },
   receiveBMsg(state, payload) {
     // 將B組件的數據存放於state
     state.BMsg = payload.BMsg
  }
 }
 
 export default new Vuex.Store({
   state,
   mutations
 })

七、localStorage / sessionStorage

這種通信比較簡單,缺點是數據和狀態比較混亂,不太容易維護。 通過window.localStorage.getItem(key)獲取數據 通過window.localStorage.setItem(key,value)存儲數據

註意用JSON.parse() / JSON.stringify() 做數據格式轉換 localStorage / sessionStorage可以結合vuex, 實現數據的持久保存,同時使用vuex解決數據和狀態混亂問題.

$attrs$listeners

現在我們來討論一種情況, 我們一開始給出的組件關係圖中A組件與D組件是隔代關係, 那它們之前進行通信有哪些方式呢?

  1. 使用props綁定來進行一級一級的信息傳遞, 如果D組件中狀態改變需要傳遞數據給A, 使用事件系統一級級往上傳遞

  2. 使用eventBus,這種情況下還是比較適合使用, 但是碰到多人合作開發時, 代碼維護性較低, 可讀性也低

  3. 使用Vuex來進行數據管理, 但是如果僅僅是傳遞數據, 而不做中間處理,使用Vuex處理感覺有點大材小用了.

vue2.4中,為瞭解決該需求,引入了$attrs$listeners , 新增了inheritAttrs 選項。 在版本2.4以前,預設情況下,父作用域中不作為 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外),將會“回退”且作為普通的HTML特性應用在子組件的根元素上。接下來看一個跨級通信的例子:

 // app.vue
 // index.vue
 
 <template>
   <div>
     <child-com1
      :name="name"
      :age="age"
      :gender="gender"
      :height="height"
       title="程式員成長指北"
     ></child-com1>
   </div>
 </template>
 <script>
 const childCom1 = () => import("./childCom1.vue");
 export default {
   components: { childCom1 },
   data() {
     return {
       name:
您的分享是我們最大的動力!

更多相關文章
  • 基於web的機票管理系統 1 摘 要 近年來,我國發展迅速,對交通工具的需求量大幅度增加。飛機作為出行工具之一,花費時間短、用戶體驗度好,價格實惠、安全性高等優點自然成為人們的首選,這也導致等待時間長、購票效率低等一系列問題的出現,給用戶和航空公司造成嚴重困擾。面對這些問題,線上機票預訂系統顯得格外 ...
  • 在開發好用戶標簽以後,如何將標簽應用到實際其實是一個很重要的問題。只有做好產品的設計才能讓標簽發揮真正的價值,本文將介紹用戶畫像的產品化過程。 一、標簽展示 首先是標簽展示功能,這個主要供業務人員和研發人員使用,是為了更直觀的看見整個的用戶標簽體系。 不同的標簽體系會有不同的層級,那麼這個頁面的設計 ...
  • Android自家的,又可以省去findviewbyid(),而且Butterknife上大神都已經推薦使用的,還有什麼理由不去改寫呢。build.gradle中開啟viewBinding功能。Activity 綁定private lateinit var mBinding: ActivityEbo... ...
  • 原文鏈接:https://www.cnblogs.com/qiyer/、https://www.cnblogs.com/qiyer/p/7442680.html ...
  • 序言 疫情基本控制,同時面試也漸漸開始了,以下iOS面試題僅供參考,畢竟面試是不可控的,但懂得越多,自然機會越大! 位元組一面內容: 1、 自我介紹 2、 介紹一下簡歷中的一個項目 3、 面向對象的三個要素 4、 多態? 5、 Java,python,OC運行效率孰高? 6、 Property,其中c ...
  • 新聞 谷歌發佈全新AR技術 單攝像頭即可實現AR景深感應 谷歌發佈首款基於Android 11開發者預覽版的Android TV版 代號“sabrina”:新Android TV電視棒將採用鵝卵石造型 Android 12曝光:谷歌欲全面拋棄對32位的支持 Android 11中的“對話”功能可能不 ...
  • Android app 本地設置信息的保存與調用。preferences.getString後面的文本是調用失敗後的預設顯示值。儲存值一定要實例化一個Editor出來,如果直接使用.edit().putString()不是不可以,但會每次調用都多出一個實例。最後記得要editor.apply()執行... ...
  • 基本類型(棧數據) String Number Boolean null undefined symbol(ES6) 引用類型(堆數據) Array Object Function Date RegExp 等 區分 棧小堆大 1.基礎類型是放置在棧裡面,一般基礎類型的數據都比較小,賦值不影響自身 v ...
一周排行
  • 圖文講解,一門教學級邏輯式編程語言,NMiniKanren,的運行原理。 ...
  • 多Sheet導入教程 說明 本教程主要說明如何使用Magicodes.IE.Excel完成多個Sheet數據的Excel導入。 要點 多個相同格式的Sheet數據導入 多個不同格式的Sheet數據導入 主要步驟 1. 多個相同格式的Sheet數據導入 1.1 創建導入Sheet的Dto 主要代碼如下 ...
  • 在KeyPress事件中寫入 private void txtBoxKeyPress(object sender, KeyPressEventArgs e) { if ((e.KeyChar >= 'a' && e.KeyChar <= 'z') || (e.KeyChar >= 'A' && e. ...
  • 在 Xunit 中使用依賴註入 Intro 之前寫過一篇 xunit 的依賴註入相關的文章,但是實際使用起來不是那麼方便 今天介紹一個基於xunit和微軟依賴註入框架的“真正”的依賴註入使用方式 ——— Xunit.DependencyInjection, 來自大師的作品,讓你在測試代碼里使用依賴註 ...
  • 官網 http://www.hzhcontrols.com/ 前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kww ...
  • 在項目的實際開發過程中,我們經常會遇到Tab頁面的開發 EciTab控制項有多種使用方式: 下麵介紹Frame容器方式: 下麵介紹的Tab頁面採用的策略是 Tab頁面管理幾個子頁面,頁面組織上用Iframe管理的模式 採用Iframe的原因主要有兩個 1.開發簡單,每一個頁面都是簡單的畫面 2.性能考 ...
  • 引用的DLL MySql.Data.MySqlClient System.Data City實體 public class City { public int ID { get; set; } public string Name { get; set; } public string Countr ...
  • 案例故事: 即時通訊(IM)軟體有很多,比如企業微信,釘釘,飛書,Skype, 微軟的Lync等, 這些軟體現在都很牛,還能監控誰誰在不在電腦旁工作,誰誰誰提前下班溜了。。。 一次偶然的機會,有個妹子請教我,她每天都想準時18點下班, 她問我如何做到: 假裝企業微信線上,併在2個小時後(20點)準時 ...
  • 一.官方文檔 https://pypi.org/project/muggle-ocr/ 二模塊安裝 pip install muggle-ocr # 因模塊過新,阿裡/清華等第三方源可能尚未更新鏡像,因此手動指定使用境外源,為了提高依賴的安裝速度,可預先自行安裝依賴:tensorflow/numpy ...
  • 前言 ​ 關於 Python 這個欄目,咕了幾個月了,今天講講如何發送驗證碼並驗證。 ​ 因為部分原因,寫這篇文章的時候心情是不太好的,播放首歌吧。 代碼 導入 導入yagmail,random和time庫 import yagmail,random,time #導入 yagmail , rando ...