初體驗(yàn)
Vue.js 在現(xiàn)今使用有多廣泛不用多說(shuō),而 Vue 的一大特點(diǎn)就是組件化做入。本期要講的冒晰,便是 Vue 組件間通信方式的總結(jié),這也幾乎是近年 Vue 面試中的必考題竟块。注:文中示例都基于 Vue 腳手架講解壶运,會(huì)用到一些?Element UI?示例。
【前端進(jìn)階之路】會(huì)作為一個(gè)新系列連載浪秘,后續(xù)會(huì)更多優(yōu)質(zhì)前端內(nèi)容蒋情,感興趣的同學(xué)不妨關(guān)注一下。
①組件
組件是可以復(fù)用的 Vue 實(shí)例耸携。 —?Vue 官方文檔棵癣;
在進(jìn)入主題之前,還是決定先簡(jiǎn)單聊聊組件夺衍。在 Vue 中狈谊,根據(jù)注冊(cè)方式的不同,可以分為:
?????局部組件 (局部注冊(cè))
?????全局組件 (全局注冊(cè))
顧名思義沟沙,全局注冊(cè)的組件河劝,可以用在 Vue 實(shí)例的任意模板中。但是帶來(lái)的隱患是矛紫,在 webpack 模塊化構(gòu)建時(shí)赎瞎,即便你沒(méi)有在項(xiàng)目中使用這個(gè)組件,依然會(huì)打包到最終的項(xiàng)目代碼中含衔。而局部組件煎娇,則需要在使用到的實(shí)例中注冊(cè)該組件二庵。
根據(jù)應(yīng)用場(chǎng)景的不同,又可以分為:
????1.頁(yè)面組件:我們使用 Vue 時(shí)缓呛,每個(gè)路由代表的頁(yè)面催享,都可以稱之為組件。
????2.基礎(chǔ)組件:就像上面栗子中的 Icon 組件哟绊,就是一個(gè)典型的基礎(chǔ)組件因妙。基本上不摻雜業(yè)務(wù)邏輯票髓,在項(xiàng)目中可能被大量使用攀涵,易于移植。類似的基礎(chǔ)組件還有 Button洽沟、Input 等以故,常見(jiàn)于各類 UI 組件庫(kù)。
????3.業(yè)務(wù)組件:業(yè)務(wù)組件和項(xiàng)目具體的業(yè)務(wù)邏輯有大量耦合裆操,一般抽離于當(dāng)前項(xiàng)目怒详。
以上就是組件的簡(jiǎn)單介紹,那我們到底為什么要推崇組件化踪区?組件化有什么好處昆烁?復(fù)用?我個(gè)人認(rèn)為組件化最大的好處缎岗,便是解耦静尼,易于項(xiàng)目管理。所以在大型項(xiàng)目管理中传泊,組件化是非常有必要的鼠渺。當(dāng)然,這并不是今天學(xué)習(xí)的重點(diǎn)眷细,以后有機(jī)會(huì)再聊系冗。
正因?yàn)樵?Vue 中處處都是組件,而我們也偏向于組件化薪鹦、模塊化。那我們?cè)谝欢呀M件中惯豆,便需要解決一個(gè)問(wèn)題 — 組件間通信池磁。下面,我們就進(jìn)入今天的主題楷兽,Vue 的組件間通信地熄。
②組件間通信
組件間通信是我們?cè)?Vue 項(xiàng)目中不可避免的問(wèn)題,深刻了解了 Vue 組件間通信的幾種方式芯杀,才能讓我們?cè)谔幚砀鞣N交互問(wèn)題時(shí)游刃有余端考。
Props
Vue 中雅潭,最基本的通信方式就是 Props,它是父子組件通信中父組件傳值給子組件的一種方式却特。它允許以數(shù)組形式接收扶供,但是更推薦你開(kāi)啟類型檢查的形式。更詳細(xì)的類型檢查前往?vue prop文檔裂明。
我們都知道椿浓,Props 是單向數(shù)據(jù)流,這是 Vue 為了避免子組件意外改變父組件的狀態(tài)闽晦,從而導(dǎo)致數(shù)據(jù)流向難以理解而做出的限制扳碍。所以 Vue 推薦需要改動(dòng)的時(shí)候,通過(guò)改變父組件的值從而觸發(fā) Props 的響應(yīng)仙蛉∷癯ǎ或者,我們可以在接收非引用類型的值時(shí)荠瘪,使用子組件自身的 data 做一次接收夯巷。
為什么是非引用類型呢,因?yàn)樵?JavaScript 中巧还,引用類型的賦值鞭莽,實(shí)際是內(nèi)存地址的傳遞。所以上面栗子中的簡(jiǎn)單賦值麸祷,顯然會(huì)指向同一個(gè)內(nèi)存地址澎怒,所以如果是數(shù)組或是對(duì)象,你可能需要一次深拷貝阶牍。
上面這個(gè)操作有一些缺陷喷面,不能序列化函數(shù)、undefined走孽、循環(huán)引用等……
事實(shí)上惧辈,在 Props 是引用類型時(shí),單獨(dú)修改對(duì)象磕瓷、數(shù)組的某個(gè)屬性或下標(biāo)盒齿,Vue 并不會(huì)拋出錯(cuò)誤。當(dāng)然困食,前提是你要非常清楚自己在做什么边翁,并寫好注釋,防止你的小伙伴們疑惑硕盹。
有的同學(xué)可能知道符匾,在組件上綁定的屬性,如果沒(méi)有在組件內(nèi)部用 Props 聲明瘩例,會(huì)默認(rèn)綁定到組件的根元素上去啊胶。還是之前的栗子:
結(jié)果如下:
這是 Vue 默認(rèn)處理的甸各,而且,除了 class 和 style 采用合并策略焰坪,其它特性(如上栗 type)會(huì)替換掉原來(lái)根元素上的屬性值趣倾。當(dāng)然,我們也可以顯示的在組件內(nèi)部關(guān)閉掉這個(gè)特性:
利用 inheritAttrs琳彩,我們還可以方便的把組件綁定的其它特性誊酌,轉(zhuǎn)移到我們指定的元素上。這就需要用到下一個(gè)我們要講的?$attrs?了露乏。
attrs碧浊、listeners
我們?cè)谑褂媒M件庫(kù)的時(shí)候經(jīng)常會(huì)這么寫:
實(shí)際渲染后:
可以看到我們指定的的 placeholder 是渲染在 input 上的,但是 input 并不是根元素瘟仿。難道都用 Props 聲明后箱锐,再賦值給 input?這種情況就可以用到?$attrs?了劳较,改造一下我們之前那個(gè)栗子驹止。
可以看到粤铭,type 已經(jīng)轉(zhuǎn)移到了子元素 input 標(biāo)簽上垢油,但是 class 沒(méi)有。這是因?yàn)閕nheritAttrs: false選項(xiàng)不會(huì)影響 style 和 class 的綁定虱肄。可以看出$attrs則是將沒(méi)有被組件內(nèi)部 Props 聲明的傳值(也叫非 Props 特性)收集起來(lái)的一個(gè)對(duì)象墓捻,再通過(guò) v-bind 將其綁定在指定元素上抖仅。這也是 Element 等組件庫(kù)采用的策略。
這里需要注意一點(diǎn)砖第,通過(guò) $attrs 指定給元素的屬性撤卢,不會(huì)與該元素原有屬性發(fā)生合并或替換,而是以原有屬性為準(zhǔn)梧兼。舉個(gè)例子放吩,假如我將上述 input 的 type 默認(rèn)設(shè)置為 password。
則不會(huì)采用 $attrs 中的 type: 'text'羽杰,將以 password 為準(zhǔn)渡紫,所以如果需要默認(rèn)值的屬性,建議不要用這種方式考赛。
$listeners同$attrs類似腻惠,可以看做是一個(gè)包含了組件上所有事件監(jiān)聽(tīng)器(包括自定義事件、不包括.native修飾的事件)的對(duì)象欲虚。它也支持上述的寫法,適用于將事件安放于組件內(nèi)指定元素上悔雹。
給之前的栗子綁定一個(gè)聚焦事件复哆,在子組件中通過(guò)$listeners綁定給 input欣喧,則會(huì)在 input 聚焦時(shí)觸發(fā)。
那么除了用在這種給組件內(nèi)指定元素綁定特性和事件的情況梯找,還有哪些場(chǎng)景可以用到呢唆阿?官方說(shuō)明:在創(chuàng)建更高層次的組件時(shí)非常有用。比如在祖孫組件中傳遞數(shù)據(jù)锈锤,在孫子組件中觸發(fā)事件后要在祖輩中做相應(yīng)更新驯鳖。我們繼續(xù)之前的栗子:在孫輩組件觸發(fā)點(diǎn)擊事件,然后在祖輩中修改相應(yīng)的 data久免。
這樣就能很方便的在多級(jí)組件的子級(jí)組件中浅辙,快速訪問(wèn)到父組件的數(shù)據(jù)和方法。正如在剛才的例子中阎姥,button 點(diǎn)擊時(shí)记舆,是直接調(diào)用的 communication.vue 中定義的方法。
總結(jié):
1呼巴、子組件觸達(dá)父組件的方式:Props泽腮、$parent、$attrs衣赶、$listeners诊赊、provide 和 inject、$dispatch
2府瞄、父組件觸達(dá)子組件的方式:$emit和$on碧磅、$children、$ref摘能、broadcast
3续崖、全局通信:EventBus、Vuex