Vue - $attrs及$listeners屬性實(shí)現(xiàn)多級(jí)嵌套組件通信

前言

組件是 vue.js 最強(qiáng)大的功能之一市俊,而組件實(shí)例的作用域是相互獨(dú)立的铣鹏,這就意味著不同組件之間的數(shù)據(jù)無法相互引用。

如上圖所示刽宪,A 和 B厘贼、B 和 C都是父子關(guān)系,A 和 C 是隔代關(guān)系(可能隔多代)圣拄。針對不同的使用場景嘴秸,如何選擇行之有效的通信方式?

A 組件與 B 組件之間的通信: (父子組件)

如上圖所示庇谆,A岳掐、B、C三個(gè)組件依次嵌套饭耳,按照 Vue 的開發(fā)習(xí)慣串述,父子組件通信可以通過以下三種方式實(shí)現(xiàn):

1、A To B 通過props的方式向子組件傳遞數(shù)據(jù)寞肖,B To A 通過在 B 組件中 $emit, A 組件中 v-on 的方式實(shí)現(xiàn)數(shù)據(jù)傳遞

2纲酗、通過設(shè)置全局Vuex共享狀態(tài),通過 computed 計(jì)算屬性和 commit mutation的方式實(shí)現(xiàn)數(shù)據(jù)的獲取和更新新蟆,以達(dá)到父子組件通信的目的觅赊。

3、Vue Event Bus琼稻,使用Vue的實(shí)例吮螺,實(shí)現(xiàn)事件的監(jiān)聽和發(fā)布,實(shí)現(xiàn)組件之間的傳遞。

往往數(shù)據(jù)在不需要全局的情況而僅僅是父子組件通信時(shí)规脸,使用第一種方式即可滿足坯约。

A 組件與 C 組件之間的通信: (跨多級(jí)的組件嵌套關(guān)系)

如上圖,A 組件與 C 組件之間屬于跨多級(jí)的組件嵌套關(guān)系莫鸭,以往兩者之間如需實(shí)現(xiàn)通信闹丐,往往通過以下方式實(shí)現(xiàn):

  1. 借助 B 組件的中轉(zhuǎn),從上到下props依次傳遞被因,從下至上卿拴,$emit事件的傳遞,達(dá)到跨級(jí)組件通信的效果
  2. 借助Vuex的全局狀態(tài)共享
  3. Vue Event Bus梨与,使用Vue的實(shí)例堕花,實(shí)現(xiàn)事件的監(jiān)聽和發(fā)布,實(shí)現(xiàn)組件之間的傳遞粥鞋。

多級(jí)組件嵌套需要傳遞數(shù)據(jù)時(shí)缘挽,通常使用的方法是通過 vuex。但如果僅僅是傳遞數(shù)據(jù)呻粹,而不做中間處理壕曼,使用 vuex 處理,未免有點(diǎn)大材小用等浊。為此 Vue2.4 版本提供了另一種方法attrs/listeners

props/attrs/$listeners介紹

$props:當(dāng)前組件接收到的 props 對象腮郊,Vue 實(shí)例代理了對其 props 對象屬性的訪問。

**attrs:**包含了父作用域中不被 prop 所識(shí)別 (且獲取) 的特性綁定 (class 和 style 除外)筹燕。當(dāng)一個(gè)組件沒有聲明任何 prop 時(shí)轧飞,這里會(huì)包含所有父作用域的綁定 (class 和 style 除外),并且可以通過 v-bind="attrs" 傳入內(nèi)部組件撒踪。通常配合 interitAttrs 選項(xiàng)一起使用过咬。 通俗的理解為:子輩可以通過$attrs將未在自己組件內(nèi)注冊的祖輩傳遞下來的參數(shù)接收來,并傳遞孫輩制妄。

inheritAttrs:默認(rèn)情況下父作用域的不被認(rèn)作 props 的特性綁定 (attribute bindings) 將會(huì)“回退”且作為普通的 HTML 特性應(yīng)用在子組件的根元素上援奢。通過設(shè)置 inheritAttrs 為 false,這些默認(rèn)行為將會(huì)被去掉忍捡。而通過實(shí)例屬性 $attrs 可以讓這些特性生效,且可以通過 v-bind 顯性的綁定到非根元素上切黔。

**listeners:**包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽器砸脊。它可以通過 v-on="listeners" 傳入內(nèi)部組件

代碼示例

A組件(index.vue)

<template>
    <div>
        <h1>我是parent</h1>
        <hr>
        <child1 
            :foo="foo" 
            :boo="boo" 
            :coo="coo" 
            :doo="doo" 
            title="跨級(jí)組件通信" 
            @test1="test1" 
            @test2="test2"
        ></child1>
    </div>
</template>
<script>
    const child1 = () => import("./child1.vue");
    export default {
        components: { child1 },
        data() {
            return {
                foo: "foo",
                boo: "boo",
                coo: "coo",
                doo: "doo"
            };
        },
        methods: {
            test1() {
                alert('test1')
            },
            test2() {
                alert('test2')
            }
        },
    };
</script>

B組件(child1.vue)

<template>
    <div>
        <h1>我是child1</h1>
        <p>props: {{ $props }}</p>
        <p>attrs: {{ $attrs }}</p>
        <button @click="toParent1()">觸發(fā)test1方法</button>
        <hr>
        <child2 v-bind="$attrs" v-on="$listeners"></child2>
    </div>
</template>
<script>
    const child2 = () => import("./child2.vue");
    export default {
        components: {
            child2
        },
        inheritAttrs: false, // 可以關(guān)閉自動(dòng)掛載到組件根元素上的沒有在props聲明的屬性
        props: {
            foo: String // foo作為props屬性綁定
        },
        created() {
            console.log(this.$attrs); // { "boo": "boo", "coo": "coo", "doo": "doo", "title": "跨級(jí)組件通信" }
        },
        methods: {
            toParent1() {
                this.$emit('test1') // 執(zhí)行test1方法,alert彈框顯示'test1'
            }
        },
    };
</script>

C組件(child2.vue)

<template>
    <div class="border">
        <h1>我是child2</h1>
        <p>props: {{ $props }}</p>
        <p>attrs: {{ $attrs }}</p>
        <button @click="toParent2">觸發(fā)test2方法</button>
        <hr>
        <child3 v-bind="$attrs" v-on="$listeners"></child3>
    </div>
</template>
<script>
    const child3 = () => import("./child3.vue");
    export default {
        components: {
            child3
        },
        inheritAttrs: false,
        props: {
            boo: String
        },
        created() {
            console.log(this.$attrs); // { "coo": "coo", "doo": "doo", "title": "跨級(jí)組件通信" }
        },
        methods: {
            toParent2() {
                this.$emit('test2') // 執(zhí)行test2方法纬霞,alert彈框顯示'test2'
            }
        },
    };
</script>

D組件(child3.vue)

<template>
    <div class="border">
        <h1>我是child3</h1>
        <p>props: {{ $props }}</p>
        <p>attrs: {{ $attrs }}</p>
        <button @click="toParent1">觸發(fā)test1方法</button>
        <button @click="toParent2">觸發(fā)test2方法</button>
    </div>
</template>
<script>
    export default {
        props: {
            coo: String,
            title: String
        },
        methods: {
            toParent1() {
                this.$emit('test1') // 執(zhí)行test1方法凌埂,alert彈框顯示'test1'
            },
            toParent2() {
                this.$emit('test2') // 執(zhí)行test2方法,alert彈框顯示'test2'
            }
        },
    };
</script>

最終頁面展示效果

總結(jié):

A組件一共傳了四個(gè)變量foo诗芜、boo瞳抓、coo埃疫、doo,一個(gè)屬性title孩哑,兩個(gè)接收函數(shù)test1栓霜,test2

組件B中props中接收了一個(gè)foo。所以$attrs傳的是剩下的 { "boo": "boo", "coo": "coo", "doo": "doo", "title": "跨級(jí)組件通信" }

組件C中props中接收了一個(gè)boo横蜒。所以$attrs傳的是剩下的 { "coo": "coo", "doo": "doo", "title": "跨級(jí)組件通信" }

組件D中props中接收了coo和title胳蛮。所以$attrs傳的是剩下的 { "doo": "doo"}

文章每周持續(xù)更新,可以微信搜索「 前端大集錦 」第一時(shí)間閱讀丛晌,回復(fù)【視頻】【書籍】領(lǐng)取200G視頻資料和30本PDF書籍資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仅炊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子澎蛛,更是在濱河造成了極大的恐慌抚垄,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谋逻,死亡現(xiàn)場離奇詭異呆馁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)斤贰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門智哀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荧恍,你說我怎么就攤上這事瓷叫。” “怎么了送巡?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵摹菠,是天一觀的道長。 經(jīng)常有香客問我骗爆,道長次氨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任摘投,我火速辦了婚禮煮寡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘犀呼。我一直安慰自己幸撕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布外臂。 她就那樣靜靜地躺著坐儿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上貌矿,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天炭菌,我揣著相機(jī)與錄音,去河邊找鬼逛漫。 笑死黑低,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尽楔。 我是一名探鬼主播投储,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阔馋!你這毒婦竟也來了玛荞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呕寝,失蹤者是張志新(化名)和其女友劉穎勋眯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體下梢,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡客蹋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了孽江。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讶坯。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖岗屏,靈堂內(nèi)的尸體忽然破棺而出辆琅,到底是詐尸還是另有隱情,我是刑警寧澤这刷,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布婉烟,位于F島的核電站,受9級(jí)特大地震影響暇屋,放射性物質(zhì)發(fā)生泄漏似袁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一咐刨、第九天 我趴在偏房一處隱蔽的房頂上張望昙衅。 院中可真熱鬧,春花似錦定鸟、人聲如沸绒尊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春躯泰,著一層夾襖步出監(jiān)牢的瞬間谭羔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工麦向, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘟裸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓诵竭,卻偏偏與公主長得像话告,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子卵慰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355