摘要:組件的通信和和內(nèi)置的通信手段一般有兩種給元素或組件注冊引用信息訪問父子實例。有時候兩個組件之間需要進行通信,但是它們彼此不是父子組件的關系。詳情可參考參考組件之間種組件通信方式總結參考參考
組件的分類
常規(guī)頁面組件,由 vue-router 產(chǎn)生的每個頁面,它本質上也是一個組件(.vue),主要承載當前頁面的 HTML 結構,會包含數(shù)據(jù)獲取、數(shù)據(jù)整理、數(shù)據(jù)可視化等常規(guī)業(yè)務。
功能性抽象組件,不包含業(yè)務,獨立、具體功能的基礎組件,比如日期選擇器、彈窗警告等。這類組件作為項目的基礎控件,會被大量使用,因此組件的 API 進行過高強度的抽象,可以通過不同配置實現(xiàn)不同的功能。
業(yè)務組件,它不像第二類獨立組件只包含某個功能,而是在業(yè)務中被多個頁面復用的,它與獨立組件的區(qū)別是,業(yè)務組件只在當前項目中會用到,不具有通用性,而且會包含一些業(yè)務,比如數(shù)據(jù)請求;而獨立組件不含業(yè)務,在任何項目中都可以使用,功能單一,比如一個具有數(shù)據(jù)校驗功能的輸入框。
組件的關系 父子組件父子關系即是組件 A 在它的模板中使用了組件 B,那么組件 A 就是父組件,組件 B 就是子組件。
// 注冊一個子組件 Vue.component("child", { data: function(){ return { text: "我是father的子組件!" } }, template: "{{ text }}" }) // 注冊一個父組件 Vue.component("father", { template: "兄弟組件" // 在模板中使用了child組件 })
兩個組件互不引用,則為兄弟組件。
Vue.component("brother1", { template: "我是大哥" }) Vue.component("brother2", { template: "我是小弟" })
使用組件的時候:
跨級組件
就是在父子關系中,中間跨了很多個層級
組件的構成一個再復雜的組件,都是由三部分組成的:prop、event、slot,它們構成了 Vue.js 組件的 API。
屬性 propprop 定義了這個組件有哪些可配置的屬性,組件的核心功能也都是它來確定的。寫通用組件時,props 最好用對象的寫法,這樣可以針對每個屬性設置類型、默認值或自定義校驗屬性的值,這點在組件開發(fā)中很重要,然而很多人卻忽視,直接使用 props 的數(shù)組用法,這樣的組件往往是不嚴謹?shù)摹?/p> 插槽 slot
插槽 slot,它可以分發(fā)組件的內(nèi)容。和 HTML 元素一樣,我們經(jīng)常需要向一個組件傳遞內(nèi)容,像這樣:
Something bad happened.
可能會渲染出這樣的東西:
Error!Something bad happended.
幸好,Vue 自定義的
Vue.component("alert-box", { template: `Error!` })
如你所見,我們只要在需要的地方加入插槽就行了——就這么簡單!
自定義事件 event兩種寫法:
在組件內(nèi)部自定義事件event
通過 $emit,就可以觸發(fā)自定義的事件 on-click ,在父級通過 @on-click 來監(jiān)聽:
用事件修飾符 .native直接在父級聲明
所以上面的示例也可以這樣寫:
如果不寫 .native 修飾符,那上面的 @click 就是自定義事件 click,而非原生事件 click,但我們在組件內(nèi)只觸發(fā)了 on-click 事件,而不是 click,所以直接寫 @click 會監(jiān)聽不到。
組件的通信 ref和$parent和$childrenVue.js 內(nèi)置的通信手段一般有兩種:
ref:給元素或組件注冊引用信息;
$parent / $children:訪問父 / 子實例。
用 ref 來訪問組件(部分代碼省略):
// component-a export default { data () { return { title: "Vue.js" } }, methods: { sayHello () { window.alert("Hello"); } } }
$parent 和 $children 類似,也是基于當前上下文訪問父組件或全部子組件的。
這兩種方法的弊端是,無法在跨級或兄弟間通信,比如下面的結構:
// parent.vue
我們想在 component-a 中,訪問到引用它的頁面中(這里就是 parent.vue)的兩個 component-b 組件,那這種情況下,是暫時無法實現(xiàn)的,后面會講解到方法。
provide / inject一種無依賴的組件通信方法:Vue.js 內(nèi)置的 provide / inject 接口
provide / inject 是 Vue.js 2.2.0 版本后新增的 API,在文檔中這樣介紹 :
這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在起上下游關系成立的時間里始終生效。如果你熟悉 React,這與 React 的上下文特性很相似。
provide 和 inject 主要為高階插件/組件庫提供用例。并不推薦直接用于應用程序代碼中。
假設有兩個組件: A.vue 和 B.vue,B 是 A 的子組件:
// A.vue export default { provide: { name: "Aresn" } } // B.vue export default { inject: ["name"], mounted () { console.log(this.name); // Aresn } }
需要注意的是:
provide 和 inject 綁定并不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監(jiān)聽的對象,那么其對象的屬性還是可響應的。
只要一個組件使用了 provide 向下提供數(shù)據(jù),那其下所有的子組件都可以通過 inject 來注入,不管中間隔了多少代,而且可以注入多個來自不同父級提供的數(shù)據(jù)。需要注意的是,一旦注入了某個數(shù)據(jù),那這個組件中就不能再聲明 這個數(shù)據(jù)了,因為它已經(jīng)被父級占有。
provide / inject API 主要解決了跨級組件間的通信問題,不過它的使用場景,主要是子組件獲取上級組件的狀態(tài),跨級組件間建立了一種主動提供與依賴注入的關系。然后有兩種場景它不能很好的解決:
父組件向子組件(支持跨級)傳遞數(shù)據(jù);
子組件向父組件(支持跨級)傳遞數(shù)據(jù)。
這種父子(含跨級)傳遞數(shù)據(jù)的通信方式,Vue.js 并沒有提供原生的 API 來支持,下面介紹一種在父子組件間通信的方法 dispatch 和 broadcast。
$attrs和$listeners如果父組件A下面有子組件B,組件B下面有組件C,這時如果組件A想傳遞數(shù)據(jù)給組件C怎么辦呢? Vue 2.4開始提供了$attrs和$listeners來解決這個問題,能夠讓組件A之間傳遞消息給組件C。
Vue.component("C",{ template:`派發(fā)與廣播——自行實現(xiàn) dispatch 和 broadcast 方法`, methods:{ passCData(val){ //觸發(fā)父組件A中的事件 this.$emit("getCData",val) } } }) Vue.component("B",{ data(){ return { mymessage:this.message } }, template:``, props:["message"],//得到父組件傳遞過來的數(shù)據(jù) methods:{ passData(val){ //觸發(fā)父組件中的事件 this.$emit("getChildData",val) } } }) Vue.component("A",{ template:` `, data(){ return { message:"hello", messagec:"hello c" //傳遞給c組件的數(shù)據(jù) } }, methods:{ getChildData(val){ console.log("這是來自B組件的數(shù)據(jù)") }, //執(zhí)行C子組件觸發(fā)的事件 getCData(val){ console.log("這是來自C組件的數(shù)據(jù):"+val) } } }) var app=new Vue({ el:"#app", template:` ` })this is parent compoent!
要實現(xiàn)的 dispatch 和 broadcast 方法,將具有以下功能:
在子組件調用 dispatch 方法,向上級指定的組件實例(最近的)上觸發(fā)自定義事件,并傳遞數(shù)據(jù),且該上級組件已預先通過 $on 監(jiān)聽了這個事件;
相反,在父組件調用 broadcast 方法,向下級指定的組件實例(最近的)上觸發(fā)自定義事件,并傳遞數(shù)據(jù),且該下級組件已預先通過 $on 監(jiān)聽了這個事件。
// 部分代碼省略 import Emitter from "../mixins/emitter.js" export default { mixins: [ Emitter ], methods: { handleDispatch () { this.dispatch(); // ① }, handleBroadcast () { this.broadcast(); // ② } } }
//emitter.js 的代碼: function 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])); } }); } 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(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); } } };
因為是用作 mixins 導入,所以在 methods 里定義的 dispatch 和 broadcast 方法會被混合到組件里,自然就可以用 this.dispatch 和 this.broadcast 來使用。
這兩個方法都接收了三個參數(shù),第一個是組件的 name 值,用于向上或向下遞歸遍歷來尋找對應的組件,第二個和第三個就是上文分析的自定義事件名稱和要傳遞的數(shù)據(jù)。
可以看到,在 dispatch 里,通過 while 語句,不斷向上遍歷更新當前組件(即上下文為當前調用該方法的組件)的父組件實例(變量 parent 即為父組件實例),直到匹配到定義的 componentName 與某個上級組件的 name 選項一致時,結束循環(huán),并在找到的組件實例上,調用 $emit 方法來觸發(fā)自定義事件 eventName。broadcast 方法與之類似,只不過是向下遍歷尋找。
來看一下具體的使用方法。有 A.vue 和 B.vue 兩個組件,其中 B 是 A 的子組件,中間可能跨多級,在 A 中向 B 通信:
// B.vue export default { name: "componentB", created () { this.$on("on-message", this.showMessage); }, methods: { showMessage (text) { window.alert(text); } } }
同理,如果是 B 向 A 通信,在 B 中調用 dispatch 方法,在 A 中使用 $on 監(jiān)聽事件即可。
以上就是自行實現(xiàn)的 dispatch 和 broadcast 方法。
它適用于以下場景:
由一個組件,向上找到最近的指定組件;
由一個組件,向上找到所有的指定組件;
由一個組件,向下找到最近的指定組件;
由一個組件,向下找到所有指定的組件;
由一個組件,找到指定組件的兄弟組件。
5 個不同的場景,對應 5 個不同的函數(shù),實現(xiàn)原理也大同小異。
向上找到最近的指定組件——findComponentUpward// 由一個組件,向上找到最近的指定組件 function findComponentUpward (context, componentName) { let parent = context.$parent; let name = parent.$options.name; while (parent && (!name || [componentName].indexOf(name) < 0)) { parent = parent.$parent; if (parent) name = parent.$options.name; } return parent; } export { findComponentUpward };
比如下面的示例,有組件 A 和組件 B,A 是 B 的父組件,在 B 中獲取和調用 A 中的數(shù)據(jù)和方法:
組件 A
向上找到所有的指定組件——findComponentsUpward組件 B
// 由一個組件,向上找到所有的指定組件 function findComponentsUpward (context, componentName) { let parents = []; const parent = context.$parent; if (parent) { if (parent.$options.name === componentName) parents.push(parent); return parents.concat(findComponentsUpward(parent, componentName)); } else { return []; } } export { findComponentsUpward };向下找到最近的指定組件——findComponentDownward
// 由一個組件,向下找到最近的指定組件 function findComponentDownward (context, componentName) { const childrens = context.$children; let children = null; if (childrens.length) { for (const child of childrens) { const name = child.$options.name; if (name === componentName) { children = child; break; } else { children = findComponentDownward(child, componentName); if (children) break; } } } return children; } export { findComponentDownward };向下找到所有指定的組件——findComponentsDownward
// 由一個組件,向下找到所有指定的組件 function findComponentsDownward (context, componentName) { return context.$children.reduce((components, child) => { if (child.$options.name === componentName) components.push(child); const foundChilds = findComponentsDownward(child, componentName); return components.concat(foundChilds); }, []); } export { findComponentsDownward };找到指定組件的兄弟組件——findBrothersComponents
// 由一個組件,找到指定組件的兄弟組件 function findBrothersComponents (context, componentName, exceptMe = true) { let res = context.$parent.$children.filter(item => { return item.$options.name === componentName; }); let index = res.findIndex(item => item._uid === context._uid); if (exceptMe) res.splice(index, 1); return res; } export { findBrothersComponents };
相比其它 4 個函數(shù),findBrothersComponents 多了一個參數(shù) exceptMe,是否把本身除外,默認是 true。尋找兄弟組件的方法,是先獲取 context.$parent.$children,也就是父組件的全部子組件,這里面當前包含了本身,所有也會有第三個參數(shù) exceptMe。Vue.js 在渲染組件時,都會給每個組件加一個內(nèi)置的屬性 _uid,這個 _uid 是不會重復的,借此我們可以從一系列兄弟組件中把自己排除掉。
Event Bus有時候兩個組件之間需要進行通信,但是它們彼此不是父子組件的關系。在一些簡單場景,你可以使用一個空的 Vue 實例作為一個事件總線中心(central event bus):
//中央事件總線 var bus=new Vue(); var app=new Vue({ el:"#app", template:`vuex處理組件之間的數(shù)據(jù)交互` }) // 在組件 brother1 的 methods 方法中觸發(fā)事件 bus.$emit("say-hello", "world") // 在組件 brother2 的 created 鉤子函數(shù)中監(jiān)聽事件 bus.$on("say-hello", function (arg) { console.log("hello " + arg); // hello world })
如果業(yè)務邏輯復雜,很多組件之間需要同時處理一些公共的數(shù)據(jù),這個時候才有上面這一些方法可能不利于項目的維護,vuex的做法就是將這一些公共的數(shù)據(jù)抽離出來,然后其他組件就可以對這個公共數(shù)據(jù)進行讀寫操作,這樣達到了解耦的目的。 詳情可參考:https://vuex.vuejs.org/zh-cn/
參考 vue組件之間8種組件通信方式總結
參考 https://github.com/iview/ivie...
參考 https://github.com/iview/ivie...
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101958.html
摘要:本身提供哪幾種通信方式首先靈感源于,支持雙向綁定,本質還是單向數(shù)據(jù)流。跟一樣,組件間最基本的數(shù)據(jù)流是通過向子組件傳遞數(shù)據(jù)。但是在卻很少使用,因為組件可以自定義事件,即后面的組件間通信方式其實就是訂閱發(fā)布模式。 例子是在 jsrun.net 平臺編寫,不支持移動端平臺,所以本文建議在 PC 端進行閱讀。 Vue 是數(shù)據(jù)驅動的視圖框架,那么組件間的數(shù)據(jù)通信是必然的事情,那么組件間如何進行數(shù)...
摘要:開篇的簡單介紹和演示的開發(fā)精髓組件組件的三個組件之間的通信方式實例講解銖寶益幫助中心前端組件開篇的簡單介紹和演示發(fā)布于年,是一個漸進式的框架,同時也是一個輕量級的框架,它只關心數(shù)據(jù),從而讓開發(fā)者不用過多的關注的改變和操作,的作者為尤雨溪, 開篇:vue.js的簡單介紹和演示 vue的開發(fā)精髓-組件 vue組件的三個API:prop、event、slot 組件之間的通信方式 實例講解:銖寶益幫...
摘要:掛載到添加文件第一個參數(shù)是事件對象,第二個參數(shù)是接收到消息信息,可以是任意類型事件訂閱監(jiān)聽當前實例上的自定義事件。取消事件訂閱,移除自定義事件監(jiān)聽器。 EventBus EventBus是一種發(fā)布/訂閱事件設計模式的實踐。在vue中適用于跨組件簡單通信,不適應用于復雜場景多組件高頻率通信,類似購物車等場景狀態(tài)管理建議采用vuex。 掛載EventBus到vue.prototype 添加...
摘要:使用也有很長一段時間但是一直以來都沒對其組件之間的通信做一個總結這次就借此總結一下。引用信息將會注冊在父組件的對象上。 使用Vue也有很長一段時間,但是一直以來都沒對其組件之間的通信做一個總結,這次就借此總結一下。 父子組件之間的通信 1)props和$emit 父組件通過props將數(shù)據(jù)下發(fā)給props,子組件通過$emit來觸發(fā)自定義事件來通知父組件進行相應的操作 具體代碼如下: ...
摘要:上圖是二月份前端框架排名,位居第一,排名第三。我們認為前端模板和組件代碼是緊密相連的。直到最近看了文檔,才發(fā)現(xiàn)另有蹊蹺。 歡迎大家關注騰訊云技術社區(qū)-segmentfault官方主頁,我們將持續(xù)在博客園為大家推薦技術精品文章哦~ 紀俊,從事Web前端開發(fā)工作,2016年加入騰訊OMG廣告平臺產(chǎn)品部,喜歡研究前端技術框架。 這里要討論的話題,不是前端框架哪家強,因為在 Vue 官網(wǎng)就已經(jīng)...
閱讀 3034·2023-04-25 18:06
閱讀 3289·2021-11-22 09:34
閱讀 2863·2021-08-12 13:30
閱讀 2049·2019-08-30 15:44
閱讀 1666·2019-08-30 13:09
閱讀 1634·2019-08-30 12:45
閱讀 1720·2019-08-29 11:13
閱讀 3614·2019-08-28 17:51