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

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

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:
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基於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 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...