您好,登錄后才能下訂單哦!
本篇內容主要講解“Vue組件間如何通信”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Vue組件間如何通信”吧!
vue的兩大特性是響應式編程和組件化。組件(Component)是 Vue 最核心的功能,但是各個組件實例的作用域是相互獨立的,這表明不同組件之間的數據是無法直接相互引用的。如果想要跨組件引用數據,就需要用到組件通信了,在通信之前先要理解組件之間的關系:
如上圖所示:
父子關系:A與B,A與C,B與D,C與E
兄弟關系:B與C
隔代關系(可能隔更多代):A與D,A與E
跨級關系:B與E,D與E等
props
/$emit
父組件通過v-bind
綁定一個自定義的屬性,子組件通過props
接收父組件傳來的數據;子組件通過$emit
觸發事件,父組件用on()
或者在子組件的自定義標簽上使用v-on
來監聽子組件觸發的自定義事件,從而接收子組件傳來的數據。
下面通過一個例子來說明父組件向子組件傳值,父組件parent.vue把數據books:['JavaScript高級程序設計', 'CSS新世界', '圖解 HTTP 彩色版']
傳給子組件child.vue,并在child.vue中展示出來
// 父組件parent.vue <template> <div> <Child :books="books"/> </div> </template> <script> import Child from './components/Child.vue' export default { name: 'parent', components: { Child }, data() { return { books: ['JavaScript高級程序設計', 'CSS新世界', '圖解 HTTP 彩色版'] } } } </script>
// 子組件child.vue <template> <div> <ul> <li v-for="(item, index) in books" :key="index">{{item}}</li> </ul> </div> </template> <script> export default { props: { books: { type: Array, default: () => { return [] } } } } </script>
注意:通過props傳遞數據是單向的,父組件數據變化時會傳遞給子組件,但子組件不能通過修改props傳過來的數據來修改父組件的相應狀態,即所謂的單向數據流。
下面通過子組件點擊書籍列表,用$emit()
觸發,然后再父組件中獲取
// 子組件child.vue <template> <div> <ul> <li v-for="(item, index) in books" :key="index" @click="like(item)">{{item}}</li> </ul> </div> </template> <script> export default { props: { books: { type: Array, default: () => { return [] } } }, methods: { like(item) { this.$emit('likeBook', item) } } } </script>
// 父組件parent.vue <template> <div> <Child :books="books" @likeBook="likeBook"/> </div> </template> <script> import Child from './components/Child.vue' export default { name: 'parent', components: { Child }, data() { return { books: ['JavaScript高級程序設計', 'CSS新世界', '圖解 HTTP 彩色版'] } }, methods: { likeBook(val) { alert('我最喜歡的書籍是《' + val + '》') } } } </script>
$parent
/$children
$parent:訪問父組件實例
$children:訪問子組件實例
// 父組件parent.vue <template> <div> <Child /> <button @click="getChildData">獲取子組件數據</button> </div> </template> <script> import Child from './components/Child.vue' export default { name: 'parent', components: { Child }, data() { return { books: ['JavaScript高級程序設計', 'CSS新世界', '圖解 HTTP 彩色版'] } }, methods: { getChildData() { alert(this.$children[0].msg) } } } </script>
// 子組件child.vue <template> <div> <ul> <li v-for="(item, index) in bookLists" :key="index">{{item}}</li> </ul> </div> </template> <script> export default { name: 'child', data() { return { bookLists: [], msg: '我是子組件的值!' } }, mounted() { this.bookLists = this.$parent.books } } </script>
注意:$parent
拿到的是對象,如果是最頂層沒有父組件的情況下拿到的是undefined
;$children
拿到的是數組,如果是做底層沒有子組件的情況下,拿到的是空數組;這兩種通信方式只能用于父子組件通信
ref
ref如果在普通Dom元素上使用,引用指向的就是 DOM 元素;如果在子組件上使用,引用就指向組件實例,可以通過實例直接調用組件的方法和數據
// 父組件parent.vue <template> <div> <Child ref="child" /> <button @click="getChildData">獲取子組件數據</button> </div> </template> <script> import Child from './components/Child.vue' export default { name: 'parent', components: { Child }, methods: { getChildData() { const msg = this.$refs['child'].msg console.log(msg) this.$refs['child'].say() } } } </script>
// 子組件child.vue <script> export default { name: 'child', data() { return { msg: '我是子組件的值!' } }, methods: { say() { alert('你好,我是子組件!') } }, } </script>
provide
/inject
祖先組件通過provide
來提供變量,子孫組件通過inject
注入變量來獲取祖先組件的數據,不管子孫組件嵌套有多深, 只要調用了inject 那么就可以注入provide中的數據。下面是具體代碼:
// 父組件 <template> <div> <h2>康熙</h2> <Son /> </div> </template> <script> import Son from './components/Son.vue' export default { components: { Son }, provide() { return { FatherToSon: this.FatherToSon, FatherToGrandson: this.FatherToGrandson, } }, data() { return { FatherToSon: '我是康熙,雍正,你是我兒子!', FatherToGrandson: '我是康熙,乾隆,你是我孫子!', } } } </script>
// 子組件 <template> <div> <h2>雍正</h2> <button @click="receive">接收</button> <Grandson /> </div> </template> <script> import Grandson from './Grandson.vue' export default { components: { Grandson }, inject: ['FatherToSon'], methods: { receive() { alert(this.FatherToSon) } } } </script>
// 孫組件 <template> <div> <h2>乾隆</h2> <button @click="receive">接收</button> </div> </template> <script> export default { inject: ['FatherToGrandson'], methods: { receive() { alert(this.FatherToGrandson) } } } </script>
注意:provide/inject只能從上往下傳值,且不是響應式,若要變成響應式的數據provide需要提供函數
eventBus
的$emit
/$on
eventBus是消息傳遞的一種方式,基于一個消息中心,訂閱和發布消息的模式,稱為發布訂閱者模式。
eventBus 又稱為事件總線。在 Vue 中可使用 eventBus 來作為溝通橋梁的概念,就像是所有組件共用相同的事件中心,可向該中心注冊發送事件或接收事件,所以組件都可以上下平行地通知其他組件。
$emit('name',args)
: name:發布的消息名稱 , args:發布的消息
$on('name',fn)
: name:訂閱的消息名稱, fn: 訂閱的消息
$once('name',fn)
: name:訂閱的消息名稱, fn: 訂閱的消息。與$on相似但是只觸發一次,一旦觸發之后,監聽器就會被移除
$off('name',callback)
:name:事件名稱,callback:回調監聽器
eventbus可以實現任何組件之前的通信,下面以兄弟組件為例
// main.js // 全局添加事件總線 Vue.prototype.$bus = new Vue()
在parent.vue引入ChildA和ChildB組件,使它們成為兄弟組件
// 父組件parent.vue <template> <div> <ChildA /> <ChildB /> </div> </template> <script> import ChildA from './components/childA' import ChildB from './components/childB' export default { components: { ChildA, ChildB } } </script>
在ChildA組件中用$emit
發送事件
// ChildA組件 <template> <div> <h2>組件A</h2> <button @click="send">發送</button> </div> </template> <script> export default { methods: { // 發送事件 send() { this.$bus.$emit('message', '歡迎使用eventBus!') } } } </script>
在ChildB組件中用$on
接收ChildA發送的事件
// ChildB組件 <template> <div> <h2>組件B</h2> </div> </template> <script> export default { mounted() { // 接收事件 this.$bus.$on('message', data => { alert('我是組件B,我收到的消息為:' + data) }) }, beforeDestroy() { this.$bus.$off('message') } } </script>
注意:$on
監聽的事件不會自動移除監聽,因此在不用時最好使用$off
移除監聽以免產生問題
$attrs
/$listeners
當組件為兩級嵌套時,一般采用props
和$emit
,但遇到多級組件嵌套時這種方法就不太適用了,如果不做中間處理,只傳遞數據用vuex有點大材小用了。因此在vue2.4
中為了解決這一需求,便引入了$attrs
和$listeners
, 新增了inheritAttrs
屬性
$attrs
:當父組件傳遞了很多數據給子組件時,子組件沒有聲明props來進行接收,么子組件中的attrs屬性就包含了所有父組件傳來的數據(除開已經props聲明了的);子組件還可以使用v?bind="$attrs"
的形式將所有父組件傳來的數據(除開已經props聲明了的)傳向下一級子組件,通常和interitAttrs
屬性一起使用。
$listeners
:包含了父組件中(不含.native
修飾器的)v-on 事件監聽器,通過v-on="$listeners"
,可以將這些事件綁定給它自己的子組件
下面看一個例子:
// 父組件 <template> <div> <ChildA :name="name" :sex="sex" :age="age" @getName="getName" @getAge="getAge" /> </div> </template> <script> import ChildA from './components/childA' export default { name: 'parent', components: { ChildA, }, data() { return { name: '小明', age: 18, sex: '男' } }, methods: { // 獲取名字 getName() { console.log('我的名字是' + this.name) }, // 獲取年齡 getAge() { console.log('我今年' + this.age + '歲'); } } } </script>
// 子組件A <template> <div> <h2>組件A</h2> {{ msgA }} <hr/> <ChildB v-bind="$attrs" :height="height" v-on="$listeners" @getHeight="getHeight" /> </div> </template> <script> import ChildB from './childB.vue' export default { name: 'ChildA', components: { ChildB }, data() { return { msgA: null, height: '175cm' } }, props: { sex: { type: String, default: '' } }, mounted() { this.msgA = this.$attrs console.log('組件A獲取的$listeners:', this.$listeners) }, methods: { // 獲取身高 getHeight() { console.log('我的身高是' + this.height); } } } </script>
// 孫組件B <template> <div> <h2>組件B</h2> {{ msgB }} </div> </template> <script> export default { name: 'ChildB', data() { return { msgB: null } }, mounted() { this.msgB = this.$attrs console.log('組件B獲取的$listeners:', this.$listeners) } } </script>
$attrs獲取的結果:
$listeners獲取的結果:
如代碼和圖所示組件A中props
聲明接收了sex屬性,因此組件中$attrs
獲取的是父組件中綁定的除去sex屬性的值;組件A中使用了v-bind="$attrs"
和v-on="$listeners"
,則組件B獲取不僅是組件A中本身綁定的屬性和方法還包含組件A獲取父組件綁定的屬性和方法
如果父組件傳遞了很多參數給子組件,而子組件沒有用props完全接收,那么沒有接收的這些屬性作為普通的 HTML attribute
應用在子組件的根元素上
如果你不希望子組件的根元素繼承特性,你可以在組件的選項中設置inheritAttrs: false
以上面的組件B為例,當inheritAttrs為true(inheritAttrs默認為true)
當inheritAttrs為false時
// 孫組件B export default { name: 'ChildB', inheritAttrs: false, data() { return { msgB: null } }, mounted() { this.msgB = this.$attrs console.log('組件B獲取的$listeners:', this.$listeners) } }
Vuex
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式 + 庫。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。
狀態管理包含以下幾個部分:
狀態(State),驅動應用的數據源
視圖(View),以聲明方式將狀態映射到視圖;
操作(Actions),響應在視圖上的用戶輸入導致的狀態變化
視圖發生變化會導致數據源的改變,數據源發生變化則會改變視圖,則上面表示是一個“單向數據流”。但是當我們的應用遇到多個組件共享狀態時,單向數據流的簡潔性很容易被破壞:
多個視圖依賴于同一狀態。
來自不同視圖的行為需要變更同一狀態。
因此,為了解決這種問題我們把組件的共享狀態抽取出來,以一個全局單例模式管理。在這種模式下,我們的組件樹構成了一個巨大的“視圖”,不管在樹的哪個位置,任何組件都能獲取狀態或者觸發行為!
通過定義和隔離狀態管理中的各種概念并通過強制規則維持視圖和狀態間的獨立性,我們的代碼將會變得更結構化且易維護。
1、state
:存儲應用中需要共享的狀態,是Vuex中的唯一數據源。
2、getters
:類似Vue中的計算屬性computed
,getter 的返回值會根據它的依賴被緩存起 來,且只有當它的依賴值發生了改變才會被重新計算。
3、mutations
:更改 Vuex 的 store 中的狀態(state)的唯一方法,且mutation 必須是同步函數
4、actions
:類似于 mutation,提交的是 mutation,而不是直接變更狀態;可以包含任意異步操作
5、modules
:將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割
// 父組件 <template> <div class="home"> <h2>父組件</h2> <hr/> <ChildA /> <hr/> <ChildB /> </div> </template> <script> import ChildA from './components/ChildA' import ChildB from './components/ChildB' export default { name: 'parent', components: { ChildA, ChildB } } </script>
// 子組件A <template> <div> <h2>組件A</h2> <p>A獲取的值: {{ count }}</p> <button @click="add(5)">ChildA-add</button> </div> </template> <script> export default { computed: { count() { return this.$store.state.count } }, methods: { // 改變store里count的值 add(num) { this.$store.dispatch('countAdd', num) } } } </script> <style> </style>
// 子組件B <template> <div> <h2>組件B</h2> <p>B獲取的值: {{ countB }}</p> <button @click="add(10)">ChildB-add</button> </div> </template> <script> import { mapMutations, mapGetters } from 'vuex' export default { computed: { ...mapGetters({ countB: 'getCount' }) }, methods: { ...mapMutations(['countAdd']), // 改變store里count的值 add(num) { this.countAdd(num) } } } </script> <style> </style>
store.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count: 0, }, getters: { getCount: (state) => { return state.count } }, mutations: { countAdd(state, num) { state.count += num } }, actions: { countAdd(context, num) { context.commit('countAdd', num) } }, modules: { } })
localStorage
/sessionStorage
localStorage:本地存儲對象,存儲的數據是永久性數據,頁面刷新,即使瀏覽器重啟,除非主動刪除不然存儲的數據會一直存在
sessionStorage:與localStorage相似,但是只有在當前頁面下有效,關閉頁面或瀏覽器存儲的數據將會清空
localStorage和sessionStorage常用的API:
setItem (key, value) —— 保存數據,以鍵值對的方式儲存信息。 getItem (key) —— 獲取數據,將鍵值傳入,即可獲取到對應的value值。 removeItem (key) —— 刪除單個數據,根據鍵值移除對應的信息。 clear () —— 刪除所有的數據 key (index) —— 獲取某個索引的key
// 存儲 setItem() { window.localStorage.setItem('name1', '小明') window.sessionStorage.setItem('name2', '小紅') }
// 接收 receive() { const name1 = window.localStorage.getItem('name1') const name2 = window.sessionStorage.getItem('name2') console.log(name1) // 打印結果為:小明 console.log(name2) // 打印結果為:小紅 }
localStorage和sessionStorage通過setItem()
存儲數據會自動轉換為String
類型,但是通過getItem()
其類型并不會轉換回來(localStorage和sessionStorage使用方法一樣,下面均以localStorage為例)
const num = 1 window.localStorage.setItem('num', num) const numRec = window.localStorage.getItem('num') console.log(numRec, typeof(numRec)) // 1 string
因此正確的存儲方式應該為:存儲之前用JSON.stringify()
方法將數據轉換成json字符串
形式;需要使用數據的時候用JSON.parse()
方法將之前存儲的字符串轉換成json對象
const num = 1 window.localStorage.setItem('num', JSON.stringify(num)) const obj = { name: '小紅', age: 18 } window.localStorage.setItem('obj', JSON.stringify(obj)) const numRec = JSON.parse(window.localStorage.getItem('num')) console.log(numRec, typeof(numRec)) // 1 'number' const objRec = JSON.parse(window.localStorage.getItem('obj')) console.log(objRec, typeof(objRec)) // {name: '小紅', age: 18} 'object'
注意:localStorage.setItem()和sessionStorage.setItem()不能直接存儲對象,必須使用JSON.stringify()
和JSON.parse()
轉換實現
以上8種通信方式主要應用在以下三類場景:
父子組件通信:最經常使用通信方式的是props
/$emit
,單一的父子組件通信使用$parent>
/$children
也比較方便;父組件也常使用ref
獲取子組件實例;也可使用provide
/inject
、$attrs
/$listeners
以及localStorage
/sessionStorage
兄弟組件通信:簡單的數據傳遞可使用eventBus
的$emit
/$on
;復雜的數據使用Vuex
比較方便;也可以使用localStorage
/sessionStorage
;
跨級組件通信:父子孫等嵌套組件通信方式多使用provide
/inject
和$attrs
/$listeners
;跨級組件通信的數據如果不復雜可使用eventBus
和localStorage
/sessionStorage
;如果數據復雜可使用Vuex
,但是要注意刷新界面Vuex存儲的數據會消失
到此,相信大家對“Vue組件間如何通信”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。