摘要:使,最主要的的是跨組件通信全局?jǐn)?shù)據(jù)維護(hù)。這兩種法旦發(fā)出事件后,任何組件都是可以接收到的,就近原則,且會(huì)在第次接收到后停冒泡,除返回。并且和也沒(méi)有解決兄弟組件間的通信問(wèn)題。
Vue的組件是其非常重要的系統(tǒng),組件之間的通信也是開(kāi)發(fā)中不可避免的需求
一般來(lái)說(shuō)Vue組件是以下幾種關(guān)系
A組件和B組件、B組件和C組件、B組件和D組件是父子關(guān)系,C組件和D組件是兄弟關(guān)系,A組件和C/D組件是隔代關(guān)系。
本文闡述了幾種常用的通信方式和使用場(chǎng)景
props&&emit父組件通過(guò) props 傳遞數(shù)據(jù)給子組件,子組件通過(guò) emit 發(fā)送事件傳遞數(shù)據(jù)給父組件
這種父子通信方式也就是典型的單向數(shù)據(jù)流,父組件通過(guò) props 傳遞數(shù)據(jù),子組件不能直接修改 props , 而是必須通過(guò)發(fā)送事件的方式告知父組件修改數(shù)據(jù)。
// component-a// component-b {{title}}
優(yōu)點(diǎn):易于使用,結(jié)構(gòu)清晰
缺點(diǎn):只能用于父子組件通信
ref&&$parent / $children這兩種都是直接得到組件實(shí)例,使?后可以直接調(diào)?組件的?法或訪(fǎng)問(wèn)數(shù)據(jù)
ref:給元素或組件注冊(cè)引?信息
$parent/$children:訪(fǎng)問(wèn)父/子組件
ref// component-a$parent / $children
$parent和$children都是基于當(dāng)前上下文訪(fǎng)問(wèn)父組件和子組件
// component-a// component-b
ref和$parent/$children的優(yōu)缺點(diǎn)和props&&emit相同,弊端都是無(wú)法在跨級(jí)和兄弟間通信
provide/injectref和$parent/$children在跨級(jí)通信中有一定的弊端。
Vue.js 2.2.0 版本后新增 provide / inject API
vue文檔
這對(duì)選項(xiàng)需要?起使?,以允許?個(gè)祖先組件向其所有?孫后代注??個(gè)依賴(lài),不論組件層次有多深,并在起上下游關(guān)系成?的時(shí)間?始終?效
provide 選項(xiàng)應(yīng)該是一個(gè)對(duì)象或返回一個(gè)對(duì)象的函數(shù)。該對(duì)象包含可注入其子孫的屬性。
inject 選項(xiàng)應(yīng)該是:
一個(gè)字符串?dāng)?shù)組
一個(gè)對(duì)象,對(duì)象的 key 是本地的綁定名,value 是:
在可用的注入內(nèi)容中搜索用的 key (字符串或 Symbol),或
一個(gè)對(duì)象,該對(duì)象的:
from 屬性是在可用的注入內(nèi)容中搜索用的 key (字符串或 Symbol)
default 屬性是降級(jí)情況下使用的 value
provide 和 inject 綁定并不是可響應(yīng)的。這是刻意為之的。然而,如果你傳入了一個(gè)可監(jiān)聽(tīng)的對(duì)象,那么其對(duì)象的屬性還是可響應(yīng)的。
// 父級(jí)組件提供 "foo" var Provider = { provide: { foo: "bar" }, // ... } // 子組件注入 "foo" var Child = { inject: ["foo"], created () { console.log(this.foo) // => "bar" } // ... }模擬Vuex
在做 Vue ?型項(xiàng)?時(shí),可以使? Vuex 做狀態(tài)管理。使用 provide / inject,可以模擬 達(dá)到 Vuex 的效果 。
使? Vuex,最主要的?的是跨組件通信、全局?jǐn)?shù)據(jù)維護(hù)。?如?戶(hù)的登錄信息維護(hù)、通知信息維護(hù)等全局的狀態(tài)和數(shù)據(jù)
通常vue應(yīng)用都有一個(gè)根根組件app.vue,可以?來(lái)存儲(chǔ)所有需要的全局?jǐn)?shù)據(jù)和狀態(tài),methods 等。項(xiàng)目中所有的組件其父組件都是app,通過(guò)provide將app實(shí)例暴露對(duì)外提供
接下來(lái)任何組件只要通過(guò) inject 注? app.vue 的 app 的話(huà),都可以直接通過(guò)this.app.xxx 來(lái)訪(fǎng)問(wèn) app.vue 的 data、computed、methods 等內(nèi)容
例如通過(guò)這個(gè)特性保存登錄信息
export default { provide() { return { app: this }; }, data() { return { userInfo: null }; }, methods: { getUserInfo() { // 這?通過(guò) ajax 獲取?戶(hù)信息后,賦值給this.userInfo; $.ajax("/user", data => { this.userInfo = data; }); } }, mounted() { this.getUserInfo(); } };
之后在任何??或組件,只要通過(guò) inject 注? app 后,就可以直接訪(fǎng)問(wèn) userInfo 的數(shù)據(jù)了
{{ app.userInfo }}
優(yōu)點(diǎn):
跨級(jí)注入
所有子組件都可獲取到注入的信息
缺點(diǎn):
注入的數(shù)據(jù)非響應(yīng)式
VuexVuex 是一個(gè)專(zhuān)為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。當(dāng)需要開(kāi)發(fā)開(kāi)發(fā)大型單頁(yè)應(yīng)用(SPA),就應(yīng)該考慮使用Vuex了,它能把組件的共享狀態(tài)抽取出來(lái),當(dāng)做一個(gè)全局單例模式進(jìn)行管理。這樣不管你在何處改變狀態(tài),都會(huì)通知使用該狀態(tài)的組件做出相應(yīng)修改。
Vuex官方文檔已經(jīng)給出了詳細(xì)的使用方式
優(yōu)點(diǎn):
官方集成管理庫(kù),可以處理各種場(chǎng)景的通信和狀態(tài)管理
缺點(diǎn):
需要額外引入管理庫(kù)
Bus如果不是大型項(xiàng)目,狀態(tài)管理不復(fù)雜,數(shù)據(jù)量不是很大,沒(méi)有必要使用Vuex
可以使用一個(gè)空的vue實(shí)例作為事件總線(xiàn)中間件Bus處理組件間的通信
首先在全局定義bus
let bus = new Vue(); var eventBus = { install(Vue, options) { Vue.prototype.$bus = bus; } }; Vue.use(eventBus);
然后就可以在組件中使用$on,$emit,off來(lái)監(jiān)聽(tīng),分發(fā)和銷(xiāo)毀組件
分發(fā)組件
// component-c
監(jiān)聽(tīng)組件
// component-d
最好在組件銷(xiāo)毀之前清除監(jiān)聽(tīng)事件
優(yōu)點(diǎn):
使用簡(jiǎn)單,不需要額外支持
可以實(shí)現(xiàn)跨級(jí)和兄弟間通信
缺點(diǎn):
需要在組件銷(xiāo)毀時(shí),手動(dòng)清除事件監(jiān)聽(tīng)
事件過(guò)多時(shí)比較混亂
dispatch/broadcast$dispatch 和 $broadcast 是Vue1.x中提供的API,前者?于向上級(jí)派發(fā)事件,只要是它的?級(jí)(?級(jí)或多級(jí)以上),都可以在組件內(nèi)通過(guò) $on 監(jiān)聽(tīng)到,后者相反,是由上級(jí)向下級(jí)?播事件
// 子組件 vm.$dispatch(eventName,params) // 父組件 vm.$on(eventName , (params) => { console.log(params); });
$broadcast 類(lèi)似,只不過(guò)?向相反。這兩種?法?旦發(fā)出事件后,任何組件都是可以接收到的,就近原則,?且會(huì)在第?次接收到后停?冒泡,除?返回 true 。
這2個(gè)方法在已經(jīng)被棄用,Vue官方給出的解釋是:
因?yàn)榛诮M件樹(shù)結(jié)構(gòu)的事件流方式實(shí)在是讓人難以理解,并且在組件結(jié)構(gòu)擴(kuò)展的過(guò)程中會(huì)變得越來(lái)越脆弱。這種事件方式確實(shí)不太好,我們也不希望在以后讓開(kāi)發(fā)者們太痛苦。并且$dispatch 和 $broadcast 也沒(méi)有解決兄弟組件間的通信問(wèn)題。
雖然在開(kāi)發(fā)中,沒(méi)有Vuex這樣的專(zhuān)業(yè)狀態(tài)管理工具方便好用,但是在獨(dú)立組件庫(kù)和一些特殊場(chǎng)景中,也是非常好用的一種傳遞方式。
模擬dispatch/broadcast自行模擬dispatch/broadcast無(wú)法達(dá)到與原方法一模一樣的效果,但是基本功能都是可以實(shí)現(xiàn)的,解決組件之間的通信問(wèn)題
方法有功能有向上/下找到對(duì)應(yīng)的組件,觸發(fā)指定事件并傳遞數(shù)據(jù),其下/上級(jí)組件已經(jīng)通過(guò)$on監(jiān)聽(tīng)了該事件。
首先需要正確地向上或向下找到對(duì)應(yīng)的組件實(shí)例,并在它上?觸發(fā)?法。
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); } } };
這兩個(gè)?法都接收了三個(gè)參數(shù),第?個(gè)是組件的 name 值,?于向上或向下遞歸遍歷來(lái)尋找對(duì)應(yīng)的組件,第?個(gè)和第三個(gè)就是上?分析的?定義事件名稱(chēng)和要傳遞的數(shù)據(jù)。
在 dispatch ?,通過(guò) while 語(yǔ)句,不斷向上遍歷更新當(dāng)前組件(即上下?為當(dāng)前調(diào)?該?法的組件)的?組件實(shí)例(變量 parent 即為?組件實(shí)例),直到匹配到定義的 componentName 與某個(gè)上級(jí)組件的 name 選項(xiàng)?致時(shí),結(jié)束循環(huán),并在找到的組件實(shí)例上,調(diào)? $emit ?法來(lái)觸發(fā)?定義事件 eventName 。 broadcast ?法與之類(lèi)似,只不過(guò)是向下遍歷尋找
優(yōu)點(diǎn):
使用簡(jiǎn)單
可以實(shí)現(xiàn)跨級(jí)通信
缺點(diǎn):
原生支持已經(jīng)廢除,需要自行實(shí)現(xiàn)
findComponents系列上述介紹的各種通信方法都有各自的局限性,我們可以實(shí)現(xiàn)一個(gè) findComponents 系列的方法,可以實(shí)現(xiàn)
向上找到最近的指定組件
向上找到所有的指定組件
向下找到最近的指定組件
向下找到所有指定的組件
找到指定組件的兄弟組件
5個(gè)方法都是通過(guò)遞歸和遍歷,通過(guò)組件name選項(xiàng)匹配到指定組件返回
向上找到最近的指定組件function findComponentUpward(context, componentName) { let parent = context.$parent; // 獲取父級(jí)組件 let name = parent.$options.name; // 獲取父級(jí)組件名稱(chēng) // 如果父級(jí)存在 且 父級(jí)組件 沒(méi)有name 或 name與要尋找的組件名不一致,重置parent和name,再逐級(jí)尋找 while (parent && (!name || [componentName].indexOf(name) < 0)) { parent = parent.$parent; if (parent) name = parent.$options.name; } // 逐級(jí)查找父級(jí)組件名和傳參名是否一致,返回找到的parent return parent; }
findComponentUpward 接收兩個(gè)參數(shù),第?個(gè)是當(dāng)前上下?,即你要基于哪個(gè)組件來(lái)向上尋找,?般都是基于當(dāng)前的組件,也就是傳? this;第?個(gè)參數(shù)是要找的組件的 name 。
dispatch是通過(guò)觸發(fā)和監(jiān)聽(tīng)事件來(lái)完成事件交互,findComponentUpward 會(huì)直接拿到組件的實(shí)例
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, // 遞歸逐級(jí)向上尋找 componentName)); } else { return []; } }
findComponentsUpward會(huì)返回的是?個(gè)數(shù)組,包含了所有找到的組件實(shí)例
findComponentsUpward 的使?場(chǎng)景較少
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; }
context.$children 得到的是當(dāng)前組件的全部?組件,所以需要遍歷?遍,找到有沒(méi)有匹配到的組件 name,如果沒(méi)找到,繼續(xù)遞歸找每個(gè) $children 的 $children,直到找到最近的?個(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); }, []); }
使? reduce 做累加器,并?遞歸將找到的組件合并為?個(gè)數(shù)組并返回
找到指定組件的兄弟組件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; }
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ù)的,借此我們可以從?系列兄弟組件中把??排除掉。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103608.html
摘要:本身提供哪幾種通信方式首先靈感源于,支持雙向綁定,本質(zhì)還是單向數(shù)據(jù)流。跟一樣,組件間最基本的數(shù)據(jù)流是通過(guò)向子組件傳遞數(shù)據(jù)。但是在卻很少使用,因?yàn)榻M件可以自定義事件,即后面的組件間通信方式其實(shí)就是訂閱發(fā)布模式。 例子是在 jsrun.net 平臺(tái)編寫(xiě),不支持移動(dòng)端平臺(tái),所以本文建議在 PC 端進(jìn)行閱讀。 Vue 是數(shù)據(jù)驅(qū)動(dòng)的視圖框架,那么組件間的數(shù)據(jù)通信是必然的事情,那么組件間如何進(jìn)行數(shù)...
摘要:一父組件通過(guò)的方式向子組件傳遞數(shù)據(jù),而通過(guò)子組件可以向父組件通信。而且只讀,不可被修改,所有修改都會(huì)失效并警告。 之前寫(xiě)了一篇關(guān)于vue面試總結(jié)的文章, 有不少網(wǎng)友提出組件之間通信方式還有很多, 這篇文章便是專(zhuān)門(mén)總結(jié)組件之間通信的 vue是數(shù)據(jù)驅(qū)動(dòng)視圖更新的框架, 所以對(duì)于vue來(lái)說(shuō)組件間的數(shù)據(jù)通信非常重要,那么組件之間如何進(jìn)行數(shù)據(jù)通信的呢?首先我們需要知道在vue中組件之間存在什么樣...
摘要:組件基礎(chǔ)與通信一腳手架簡(jiǎn)介與安裝之前安裝的是模塊,之后安裝的是模塊。如果是三級(jí)組件通信,該如何處理比如父組件與孫子組件通信。和,提供和注入實(shí)現(xiàn)祖先組件和后代組件之間通信。 Vue組件基礎(chǔ)與通信 一、vue cli腳手架 ① vue cli 簡(jiǎn)介與安裝 vue cli 3.0之前安裝的是vue-cli模塊,vue cli 3.0之后安裝的是@vue/cli模塊。如果已經(jīng)全局安裝了舊版本的...
摘要:雖然和都可以獲取組件實(shí)例,但是它們無(wú)法在跨級(jí)或兄弟間通信,這是它們的缺點(diǎn)。也就是在父組件中提供一個(gè)值,并且在需要使用的子孫組件中注入改值,即不僅僅是,只要是的子組件,無(wú)論隔多少代,都可以通過(guò)這個(gè)的方式注入。通過(guò)混入組件,實(shí)現(xiàn)組件間的通信。 寫(xiě)在前面 vue 的組件化應(yīng)該是其最核心的思想了,無(wú)論是一個(gè)大的頁(yè)面還是一個(gè)小的按鈕,都可以被稱(chēng)之為組件?;?Vue 的開(kāi)發(fā),就是在寫(xiě)一個(gè)個(gè)組件,...
摘要:整理種組件通信方式重點(diǎn)是梳理了前兩個(gè)父子組件通信和通信我覺(jué)得文檔里的說(shuō)明還是有一些簡(jiǎn)易我自己第一遍是沒(méi)看明白。第四種通信方式利用比較復(fù)雜可以單獨(dú)寫(xiě)一篇 整理4種Vue組件通信方式 重點(diǎn)是梳理了前兩個(gè),父子組件通信和eventBus通信,我覺(jué)得Vue文檔里的說(shuō)明還是有一些簡(jiǎn)易,我自己第一遍是沒(méi)看明白。 父子組件的通信 非父子組件的eventBus通信 利用本地緩存實(shí)現(xiàn)組件通信 Vuex...
總結(jié)一下對(duì)vue組件通信的理解和使用。 一、組件目錄結(jié)構(gòu) 父組件:app.vue 子組件:page1.vue 子組件:page2.vue 父組件 app.vue 請(qǐng)輸入單價(jià): import Page1 from ./components/page1; import Page2 from ./components/page2; export de...
閱讀 3107·2021-10-13 09:40
閱讀 3962·2021-09-22 15:51
閱讀 1508·2021-09-22 15:48
閱讀 1076·2021-09-06 15:00
閱讀 1801·2019-08-30 15:43
閱讀 2370·2019-08-29 18:35
閱讀 1683·2019-08-29 16:18
閱讀 3625·2019-08-29 12:49