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