1. 程式人生 > >Vue元件間通訊6種方式

Vue元件間通訊6種方式

摘要: 總有一款合適的通訊方式。

Fundebug經授權轉載,版權歸原作者所有。

前言

元件是 vue.js 最強大的功能之一,而元件例項的作用域是相互獨立的,這就意味著不同元件之間的資料無法相互引用。一般來說,元件可以有以下幾種關係:

如上圖所示,A 和 B、B 和 C、B 和 D 都是父子關係,C 和 D 是兄弟關係,A 和 C 是隔代關係(可能隔多代)。

針對不同的使用場景,如何選擇行之有效的通訊方式?這是我們所要探討的主題。本文總結了 vue 元件間通訊的幾種方式,如 props、$emit/$on、vuex、$parent / $children$attrs/$listeners

和 provide/inject,以通俗易懂的例項講述這其中的差別及使用場景,希望對小夥伴有些許幫助。

本文的程式碼請猛戳github 部落格,紙上得來終覺淺,大家動手多敲敲程式碼!

方法一、props/$emit

父元件 A 通過 props 的方式向子元件 B 傳遞,B to A 通過在 B 元件中 $emit, A 元件中 v-on 的方式實現。

1. 父元件向子元件傳值

接下來我們通過一個例子,說明父元件如何向子元件傳遞值:在子元件 Users.vue 中如何獲取父元件 App.vue 中的資料 users:["Henry","Bucky","Emily"]

//App.vue父元件
<template>
  <div id="app">
    <users v-bind:users="users"></users>//前者自定義名稱便於子元件呼叫,後者要傳遞資料名
  </div>
</template>
<script>
import Users from "./components/Users"
export default {
  name: 'App',
  data(){
    return{
      users:["Henry","Bucky","Emily"]
    }
  },
  components:{
    "users":Users
  }
}
//users子元件
<template>
  <div class="hello">
    <ul>
      <li v-for="user in users">{{user}}</li>//遍歷傳遞過來的值,然後呈現到頁面
    </ul>
  </div>
</template>
<script>
export default {
  name: 'HelloWorld',
  props:{
    users:{           //這個就是父元件中子標籤自定義名字
      type:Array,
      required:true
    }
  }
}
</script>

總結:父元件通過 props 向下傳遞資料給子元件。注:元件中的資料共有三種形式:data、props、computed

2. 子元件向父元件傳值(通過事件形式)

接下來我們通過一個例子,說明子元件如何向父元件傳遞值:當我們點選“Vue.js Demo”後,子元件向父元件傳遞值,文字由原來的“傳遞的是一個值”變成“子向父元件傳值”,實現子元件向父元件值的傳遞。

// 子元件
<template>
  <header>
    <h1 @click="changeTitle">{{title}}</h1>//繫結一個點選事件
  </header>
</template>
<script>
export default {
  name: 'app-header',
  data() {
    return {
      title:"Vue.js Demo"
    }
  },
  methods:{
    changeTitle() {
      this.$emit("titleChanged","子向父元件傳值");//自定義事件  傳遞值“子向父元件傳值”
    }
  }
}
</script>
// 父元件
<template>
  <div id="app">
    <app-header v-on:titleChanged="updateTitle" ></app-header>//與子元件titleChanged自定義事件保持一致
   // updateTitle($event)接受傳遞過來的文字
    <h2>{{title}}</h2>
  </div>
</template>
<script>
import Header from "./components/Header"
export default {
  name: 'App',
  data(){
    return{
      title:"傳遞的是一個值"
    }
  },
  methods:{
    updateTitle(e){   //宣告這個函式
      this.title = e;
    }
  },
  components:{
   "app-header":Header,
  }
}
</script>

總結:子元件通過 events 給父元件傳送訊息,實際上就是子元件把自己的資料傳送到父元件。

方法二、$emit/$on

這種方法通過一個空的 Vue 例項作為中央事件匯流排(事件中心),用它來觸發事件和監聽事件,巧妙而輕量地實現了任何元件間的通訊,包括父子、兄弟、跨級。當我們的專案比較大時,可以選擇更好的狀態管理解決方案 vuex。

1.具體實現方式:

    var Event=new Vue();
    Event.$emit(事件名,資料);
    Event.$on(事件名,data => {});

2.舉個例子

假設兄弟元件有三個,分別是 A、B、C 元件,C 元件如何獲取 A 或者 B 元件的資料

<div id="itany">
	<my-a></my-a>
	<my-b></my-b>
	<my-c></my-c>
</div>
<template id="a">
  <div>
    <h3>A元件:{{name}}</h3>
    <button @click="send">將資料傳送給C元件</button>
  </div>
</template>
<template id="b">
  <div>
    <h3>B元件:{{age}}</h3>
    <button @click="send">將陣列傳送給C元件</button>
  </div>
</template>
<template id="c">
  <div>
    <h3>C元件:{{name}},{{age}}</h3>
  </div>
</template>
<script>
var Event = new Vue();//定義一個空的Vue例項
var A = {
	template: '#a',
	data() {
	  return {
	    name: 'tom'
	  }
	},
	methods: {
	  send() {
	    Event.$emit('data-a', this.name);
	  }
	}
}
var B = {
	template: '#b',
	data() {
	  return {
	    age: 20
	  }
	},
	methods: {
	  send() {
	    Event.$emit('data-b', this.age);
	  }
	}
}
var C = {
	template: '#c',
	data() {
	  return {
	    name: '',
	    age: ""
	  }
	},
	mounted() {//在模板編譯完成後執行
	 Event.$on('data-a',name => {
	     this.name = name;//箭頭函式內部不會產生新的this,這邊如果不用=>,this指代Event
	 })
	 Event.$on('data-b',age => {
	     this.age = age;
	 })
	}
}
var vm = new Vue({
	el: '#itany',
	components: {
	  'my-a': A,
	  'my-b': B,
	  'my-c': C
	}
});
</script>

$on 監聽了自定義事件 data-a 和 data-b,因為有時不確定何時會觸發事件,一般會在 mounted 或 created 鉤子中來監聽。

方法三、vuex

1. 簡要介紹 Vuex 原理

Vuex 實現了一個單向資料流,在全域性擁有一個 State 存放資料,當元件要更改 State 中的資料時,必須通過 Mutation 進行,Mutation 同時提供了訂閱者模式供外部外掛呼叫獲取 State 資料的更新。而當所有非同步操作(常見於呼叫後端介面非同步獲取更新資料)或批量的同步操作需要走 Action,但 Action 也是無法直接修改 State 的,還是需要通過 Mutation 來修改 State 的資料。最後,根據 State 的變化,渲染到檢視上。

2. 簡要介紹各模組在流程中的功能:

  • Vue Components:Vue 元件。HTML 頁面上,負責接收使用者操作等互動行為,執行 dispatch 方法觸發對應 action 進行迴應。
  • dispatch:操作行為觸發方法,是唯一能執行 action 的方法。
  • actions:操作行為處理模組,由元件中的$store.dispatch('action 名稱', data1)來觸發。然後由 commit()來觸發 mutation 的呼叫 , 間接更新 state。負責處理 Vue Components 接收到的所有互動行為。包含同步/非同步操作,支援多個同名方法,按照註冊的順序依次觸發。向後臺 API 請求的操作就在這個模組中進行,包括觸發其他 action 以及提交 mutation 的操作。該模組提供了 Promise 的封裝,以支援 action 的鏈式觸發。
  • commit:狀態改變提交操作方法。對 mutation 進行提交,是唯一能執行 mutation 的方法。
  • mutations:狀態改變操作方法,由 actions 中的commit('mutation 名稱')來觸發。是 Vuex 修改 state 的唯一推薦方法。該方法只能進行同步操作,且方法名只能全域性唯一。操作之中會有一些 hook 暴露出來,以進行 state 的監控等。
  • state:頁面狀態管理容器物件。集中儲存 Vue components 中 data 物件的零散資料,全域性唯一,以進行統一的狀態管理。頁面顯示所需的資料從該物件中進行讀取,利用 Vue 的細粒度資料響應機制來進行高效的狀態更新。
  • getters:state 物件讀取方法。圖中沒有單獨列出該模組,應該被包含在了 render 中,Vue Components 通過該方法讀取全域性 state 物件。

3. Vuex 與 localStorage

vuex 是 vue 的狀態管理器,儲存的資料是響應式的。但是並不會儲存起來,重新整理之後就回到了初始狀態,具體做法應該在 vuex 裡資料改變的時候把資料拷貝一份儲存到 localStorage 裡面,重新整理之後,如果 localStorage 裡有儲存的資料,取出來再替換 store 裡的 state。

let defaultCity = "上海"
try {   // 使用者關閉了本地儲存功能,此時在外層加個try...catch
  if (!defaultCity){
    defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
  }
}catch(e){}
export default new Vuex.Store({
  state: {
    city: defaultCity
  },
  mutations: {
    changeCity(state, city) {
      state.city = city
      try {
      window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
      // 資料改變的時候把資料拷貝一份儲存到localStorage裡面
      } catch (e) {}
    }
  }
})

這裡需要注意的是:由於 vuex 裡,我們儲存的狀態,都是陣列,而 localStorage 只支援字串,所以需要用 JSON 轉換:

JSON.stringify(state.subscribeList);   // array -> string
JSON.parse(window.localStorage.getItem("subscribeList"));    // string -> array

方法四、$attrs/$listeners

1. 簡介

多級元件巢狀需要傳遞資料時,通常使用的方法是通過 vuex。但如果僅僅是傳遞資料,而不做中間處理,使用 vuex 處理,未免有點大材小用。為此 Vue2.4 版本提供了另一種方法----$attrs/$listeners

  • $attrs:包含了父作用域中不被 prop 所識別 (且獲取) 的特性繫結 (class 和 style 除外)。當一個元件沒有宣告任何 prop 時,這裡會包含所有父作用域的繫結 (class 和 style 除外),並且可以通過 v-bind="$attrs" 傳入內部元件。通常配合 interitAttrs 選項一起使用。
  • $listeners:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過 v-on="$listeners" 傳入內部元件

接下來我們看個跨級通訊的例子:

// index.vue
<template>
  <div>
    <h2>浪裡行舟</h2>
    <child-com1
      :foo="foo"
      :boo="boo"
      :coo="coo"
      :doo="doo"
      title="前端工匠"
    ></child-com1>
  </div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      foo: "Javascript",
      boo: "Html",
      coo: "CSS",
      doo: "Vue"
    };
  }
};
</script>
// childCom1.vue
<template class="border">
  <div>
    <p>foo: {{ foo }}</p>
    <p>childCom1的$attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // 可以關閉自動掛載到元件根元素上的沒有在props宣告的屬性
  props: {
    foo: String // foo作為props屬性繫結
  },
  created() {
    console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
  }
};
</script>
// childCom2.vue
<template>
  <div class="border">
    <p>boo: {{ boo }}</p>
    <p>childCom2: {{ $attrs }}</p>
    <child-com3 v-bind="$attrs"></child-com3>
  </div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue");
export default {
  components: {
    childCom3
  },
  inheritAttrs: false,
  props: {
    boo: String
  },
  created() {
    console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
  }
};
</script>
// childCom3.vue
<template>
  <div class="border">
    <p>childCom3: {{ $attrs }}</p>
  </div>
</template>
<script>
export default {
  props: {
    coo: String,
    title: String
  }
};
</script>

如上圖所示$attrs表示沒有繼承資料的物件,格式為{屬性名:屬性值}。Vue2.4 提供了$attrs , $listeners 來傳遞資料與事件,跨級元件之間的通訊變得更簡單。

簡單來說:$attrs$listeners 是兩個物件,$attrs 裡存放的是父元件中繫結的非 Props 屬性,$listeners裡存放的是父元件中繫結的非原生事件。

方法五、provide/inject

1. 簡介

Vue2.2.0 新增 API,這對選項需要一起使用,以允許一個祖先元件向其所有子孫後代注入一個依賴,不論元件層次有多深,並在起上下游關係成立的時間裡始終生效。一言而蔽之:祖先元件中通過 provider 來提供變數,然後在子孫元件中通過 inject 來注入變數。 provide / inject API 主要解決了跨級元件間的通訊問題,不過它的使用場景,主要是子元件獲取上級元件的狀態,跨級元件間建立了一種主動提供與依賴注入的關係

2. 舉個例子

假設有兩個元件: A.vue 和 B.vue,B 是 A 的子元件

// A.vue
export default {
  provide: {
    name: '浪裡行舟'
  }
}
// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // 浪裡行舟
  }
}

可以看到,在 A.vue 裡,我們設定了一個 provide: name,值為 浪裡行舟,它的作用就是將 name 這個變數提供給它的所有子元件。而在 B.vue 中,通過 inject 注入了從 A 元件中提供的 name 變數,那麼在元件 B 中,就可以直接通過 this.name 訪問這個變量了,它的值也是 浪裡行舟。這就是 provide / inject API 最核心的用法。

需要注意的是:provide 和 inject 繫結並不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監聽的物件,那麼其物件的屬性還是可響應的----vue 官方文件 所以,上面 A.vue 的 name 如果改變了,B.vue 的 this.name 是不會改變的,仍然是 浪裡行舟。

3. provide 與 inject 怎麼實現資料響應式

一般來說,有兩種辦法:

  • provide 祖先元件的例項,然後在子孫元件中注入依賴,這樣就可以在子孫元件中直接修改祖先元件的例項的屬性,不過這種方法有個缺點就是這個例項上掛載很多沒有必要的東西比如 props,methods
  • 使用 2.6 最新 API Vue.observable 優化響應式 provide(推薦)

我們來看個例子:孫元件 D、E 和 F 獲取 A 元件傳遞過來的 color 值,並能實現資料響應式變化,即 A 元件的 color 變化後,元件 D、E、F 不會跟著變(核心程式碼如下:)

// A 元件
<div>
      <h1>A 元件</h1>
      <button @click="() => changeColor()">改變color</button>
      <ChildrenB />
      <ChildrenC />
</div>
......
  data() {
    return {
      color: "blue"
    };
  },
  // provide() {
  //   return {
  //     theme: {
  //       color: this.color //這種方式繫結的資料並不是可響應的
  //     } // 即A元件的color變化後,元件D、E、F不會跟著變
  //   };
  // },
  provide() {
    return {
      theme: this//方法一:提供祖先元件的例項
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.color = color;
      } else {
        this.color = this.color === "blue" ? "red" : "blue";
      }
    }
  }
  // 方法二:使用2.6最新API Vue.observable 優化響應式 provide
  // provide() {
  //   this.theme = Vue.observable({
  //     color: "blue"
  //   });
  //   return {
  //     theme: this.theme
  //   };
  // },
  // methods: {
  //   changeColor(color) {
  //     if (color) {
  //       this.theme.color = color;
  //     } else {
  //       this.theme.color = this.theme.color === "blue" ? "red" : "blue";
  //     }
  //   }
  // }
// F 元件
<template functional>
  <div class="border2">
    <h3 :style="{ color: injections.theme.color }">F 元件</h3>
  </div>
</template>
<script>
export default {
  inject: {
    theme: {
      //函式式元件取值不一樣
      default: () => ({})
    }
  }
};
</script>

雖說 provide 和 inject 主要為高階外掛/元件庫提供用例,但如果你能在業務中熟練運用,可以達到事半功倍的效果!

方法六、$parent / $childrenref

  • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子元件上,引用就指向元件例項
  • $parent / $children:訪問父 / 子例項

需要注意的是:這兩種都是直接得到元件例項,使用後可以直接呼叫元件的方法或訪問資料。我們先來看個用 ref來訪問元件的例子:

// component-a 子元件
export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}
// 父元件
<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.title);  // Vue.js
      comA.sayHello();  // 彈窗
    }
  }
</script>

不過,這兩種方法的弊端是,無法在跨級或兄弟間通訊

// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>

我們想在 component-a 中,訪問到引用它的頁面中(這裡就是 parent.vue)的兩個 component-b 元件,那這種情況下,就得配置額外的外掛或工具了,比如 Vuex 和 Bus 的解決方案。

總結

常見使用場景可以分為三類:

  • 父子通訊: 父向子傳遞資料是通過 props,子向父是通過 events($emit);通過父鏈 / 子鏈也可以通訊($parent / $children);ref 也可以訪問元件例項;provide / inject API;$attrs/$listeners
  • 兄弟通訊: Bus;Vuex
  • 跨級通訊: Bus;Vuex;provide / inject API、$attrs/$listeners

給大家推薦一個好用的 BUG 監控工具Fundebug,歡迎免費試用!

參考文章

關於Fundebug

Fundebug專注於JavaScript、微信小程式、微信小遊戲、支付寶小程式、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟體、百姓網等眾多品牌企業。歡迎大家免費試用

相關推薦

Vue元件通訊6方式

摘要: 總有一款合適的通訊方式。 作者:浪裡行舟 Fundebug經授權轉載,版權歸原作者所有。 前言 元件是 vue.js

常見的Vue元件的傳參方式

Vue父子元件通訊的方法其實有很多,本文只是做一個總結,說說他們的優缺點,具體如何使用相關文件和網上大神已經總結的很多裡,這裡就不再說明。 1.Vuex 介紹 Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式儲存管理應用的所有元件的狀態, 並以相應的規則保證狀態以一種可

Android activity通訊方式

read flag 進程 destroy ads sage on() sting ogl Activity 通訊 Bundle 我們可以通過將數據封裝在Bundle對象中 ,然後在Intent跳轉的時候攜帶Bundle對象 bundle 本質上是使用 arrayMap實現

Android程序通訊 - 幾方式的對比總結

什麼是RPC RPC(Remote Procedure Call)即遠端過程呼叫,它是一種通過網路從遠端計算機程式上請求服務,在不需要了解底層網路技術的協議下,即可獲取計算機程序中的資料。RPC使得開發包括網路分散式多程式在內的應用程式更加容易。 RPC在OSI網路通訊7層模型中

vue - 元件通訊2

父元件--> 子元件 1. 屬性設定 父元件關鍵程式碼如下: <template> <Child :child-msg="msg"></Child> </template> 子元件關鍵程式碼如下: export de

vue-元件通訊

1、在vue中父元件是通過props傳遞資料給子元件 <child-component :prop1="父元件的資料1" :prop2="父元件的資料2"></child-component> 子元件只接受在子元件中定義過的props的值, V

vue 元件通訊 PubSub 釋出訂閱

很長一段時間在vue開發專案中元件間通訊都是通過vuex實現的,有時候開發一個小專案也要用到插入笨重的vuex來實現元件間互動,有沒有一個好的外掛可以替代vuex,emmmm~,當然有了!今天給大家介紹一下PubSubJS,PubSubJS是什麼?可以實現什麼功能?看下面的例子實現關閉彈窗m

vue元件通訊用例

父元件傳值給子元件 -- 以封裝公用slide元件為例 父元件 <template> <section class="banner"> <slide :imgList="imgList" :options="swiperOption" width="100%" h

VUE元件定義的幾方式

(1)                 區域性註冊 var app ={ //app是元件的模板名 templete:'<div>我是元件的模板,但我不知道我是那個元件的模板,在這裡你可以敲出你想要顯示的內容 例

Vue元件通訊2--Vue自定義事件

自定義事件方式一(常用) 1、給TodoHeader標籤物件繫結addTodo事件監聽 2、觸發自定義事件:addTodo  自定義事件方式二 1、給<TodoHeader>繫結addTodo事件監聽 2、觸發自定義事件:addTodo

vue元件通訊

隨著vue等框架盛行,元件開發已然成為一種趨勢,元件開發中我們經常遇到的就是父子元件通訊了。下面簡單介紹下vue中父子元件如何通訊: 一、使用props傳遞資料 元件例項的作用域是孤立的。這意味著不能 (也不應該) 在子元件的模板內直接引用父元件的資料。

Android 程序通訊方式

android系統中應用程式之間不能共享記憶體,在android SDK中提供了4種用於跨程序通訊的方式。這4種方式正好對應於android系統中4種應用程式元件:Activity、Content Provider、Broadcast和Service。 1、A

Vue元件通訊4--訊息訂閱與釋出

安裝pubsub-js npm install pubsub-js --save 優點:可以進行任意元件間的資訊傳遞。 1、釋出訊息   PubSub.publish('msg', data) 下面的例子中,msg為‘type’ , data為communicate

Vue元件通訊-Vuex

  上回說到Vue元件間通訊,最後留了一個彩蛋~~~Vuex。Vuex是另一種元件通訊的方法,這節來說說Vuex(store倉庫)。    首先Vuex需要安裝,安裝的方式有很多,在這裡就不一一細說了。我是通過npm方式安裝的: npm install vuex --save 安裝好之後需要再m

Vue.js元件通訊方式總結

  平時在使用Vue框架的業務開發中,元件不僅僅要把模板的內容進行復用,更重要的是元件之間要進行通訊。元件之間通訊分為三種:父-子;子-父;跨級元件通訊。下面,就元件間如何通訊做一些總結。 1.父元件到子元件通過props通訊 在元件中,使用選項props來宣告需要從父級元件接受的資料,props的值可以

聊聊Vue.js元件通訊的幾姿勢

寫在前面 因為對Vue.js很感興趣,而且平時工作的技術棧也是Vue.js,這幾個月花了些時間研究學習了一下Vue.js原始碼,並做了總結與輸出。 文章的原地址:https://github.com/answershuto/learnVue。 在學習過程中,為Vue加上了中文的註釋htt

Vue元件通訊方式

# Vue中元件間通訊的方式 `Vue`中元件間通訊包括父子元件、兄弟元件、隔代元件之間通訊。 ## props $emit 這種元件通訊的方式是我們運用的非常多的一種,`props`以單向資料流的形式可以很好的完成父子元件的通訊,所謂單向資料流,就是資料只能通過`props`由父元件流向子元件,而子元件並

2018年11月08日 關於Vue的父子通訊 and 子父通訊 and 任意及平行元件通訊的學習

1、父子通訊 //在html中的相關程式碼 <body> <div id="app"> <alert change_alert="再見"></alert> //如果我們想要點選按鈕的時候彈出的內容時change_alert中的“再

Vue之父子元件通訊例項講解(props、$ref、$emit)

元件是 vue.js 最強大的功能之一,而元件例項的作用域是相互獨立的,這就意味著不同元件之間的資料無法相互引用。那麼元件間如何通訊,也就成為了vue中重點知識了。這篇文章將會通過props、$ref和 $emit 這幾個知識點,來講解如何實現父子元件間通訊。   在說如何實現通訊

說說在 Vue.js 中如何實現元件通訊

1 用法 假設父元件的模板包含子元件,我們可以通過 props 來正向地把資料從父元件傳遞給子元件。props 可以是字串陣列,也可以是物件。 html: <div id="app"> <deniro-component message=""嫦娥四號"成功發射