Vue使用$attrs與$listeners實(shí)現(xiàn)多層嵌套傳遞

官方文檔中的解釋
https://cn.vuejs.org/v2/guide/components-props.html#替換-合并已有的特性

現(xiàn)有3個(gè)嵌套組件,A->B,B->C。
現(xiàn)在我么需要在A中對(duì)C的props賦值,監(jiān)聽(tīng)C的emit事件

圖片

A組件與C組件通信,我們有多少種解決方案?

  1. 我們使用vuex來(lái)進(jìn)行數(shù)據(jù)管理,依賴于vuex我們可以一次改變,任何一個(gè)組件中都能獲取。但是如果多個(gè)組件共享狀態(tài)比較少,使用vuex過(guò)于麻煩和難以維護(hù)。element-ui中大量采用此方法。
  2. 自定義vue bus事件總線,原理類似vuex,使用特別簡(jiǎn)單。bus適合碰到組件跨級(jí)兄弟組件等無(wú)明顯依賴關(guān)系的消息傳遞,原生app開(kāi)發(fā)中經(jīng)常用到,但是缺點(diǎn)是bus破壞了代碼的鏈?zhǔn)秸{(diào)用,大量的濫用將導(dǎo)致邏輯的分散,出現(xiàn)問(wèn)題后很難定位,降低了代碼可讀性。
  3. 使用B來(lái)做中轉(zhuǎn),A傳遞給B,B再給C這是最容易想到的方案,但是如果嵌套的組件過(guò)多,需要傳遞的事件和屬性較多,會(huì)導(dǎo)致代碼繁瑣,代碼維護(hù)困難。

對(duì)于我們這種情況,3種方式都有局限性

在vue2.4中,為了解決該需求,引入了$attrs$listeners,新增了inheritAttrs選項(xiàng)。我們只需要在B組件中對(duì)引入的C組件增加下面兩個(gè)屬性即可綁定所有的屬性和事件。

<C v-bind="$attrs" v-on="$listeners"></C>

A組件

<template>
<div>
  <h2>組件A 數(shù)據(jù)項(xiàng):{{myData}}</h2>
  <B @changeMyData="changeMyData" :myData="myData"></B>
</div>
</template>
<script>
import B from "./B";
export default {
  data() {
    return {
      myData: "100"
    };
  },
  components: { B },
  methods: {
    changeMyData(val) {
      this.myData = val;
    }
  }
};
</script>

B組件

<template>
  <div>
    <h3>組件B</h3>
    <C v-bind="$attrs" v-on="$listeners"></C>
  </div>
</template>
<script>
import C from "./C";
export default {
  components: { C },
};
</script>

C組件

<template>
  <div>
    <h5>組件C</h5>
    <input v-model="myc" @input="hInput" />
  </div>
</template>
<script>
export default {
  props: { myData: { String } },
  created() {
    this.myc = this.myData;  // 在組件A中傳遞過(guò)來(lái)的屬性
    console.info(this.$attrs, this.$listeners);
  },
  methods: {
    hInput() {
      this.$emit("changeMyData", this.myc); // // 在組件A中傳遞過(guò)來(lái)的事件
    }
  }
};
</script>

實(shí)際應(yīng)用

element-ui開(kāi)發(fā)的后臺(tái)項(xiàng)目中,大量使用到了el-tableel-pagination做分頁(yè)數(shù)據(jù)展示,所以我封裝一個(gè)自定義組件page-table

<template>
  <div class="page-table">
    <div class="wrapper">
      <el-table
          ref="elTable"
          :data="tableData">
        <slot/>
      </el-table>
      <div style="margin-top: 16px;overflow: hidden">
        <el-pagination
            class="page"
            :current-page="currentPage"
            layout="total, prev, pager, next, jumper"
            :total="total"
            @current-change="handleCurrentChange"/>
      </div>
    </div>
  </div>
</template>

但是這樣做的副作用是引用page-table的地方無(wú)法使用el-table屬性和事件。我們?cè)?code>page-table中把所有el-table的屬性和事件都中轉(zhuǎn)一遍?有上百個(gè)呢。

只需在el-table使用的地方加上v-on="$listeners"v-bind="$attrs"即可,使用page-table的地方即可使用所有el-table的屬性和事件。

<template>
  <div class="page-table">
    <div class="wrapper">
      <el-table
          ref="elTable"
          v-bind="$attrs"
          v-on="$listeners"
          :data="tableData">
        <slot/>
      </el-table>
      <div style="margin-top: 16px;overflow: hidden">
        <el-pagination
            class="page"
            :current-page="currentPage"
            layout="total, prev, pager, next, jumper"
            :total="total"
            @current-change="handleCurrentChange"/>
      </div>
    </div>
  </div>
</template>

我們有時(shí)候會(huì)碰到有多個(gè)兄弟組件需要傳遞參數(shù)到最外層,如有B組件包含C1和C2,都需要和A交互,定義2個(gè)props使用v-bind即可

<template>
  <div class="page-table">
    <div class="wrapper">
      <el-table
          ref="elTable"
          v-bind="table1Props"
          :data="tableData">
        <slot/>
      </el-table>
      <el-table
          ref="elTable"
          v-bind="table2Props"
          :data="tableData">
        <slot/>
      </el-table>
      <div style="margin-top: 16px;overflow: hidden">
        <el-pagination
            class="page"
            :current-page="currentPage"
            layout="total, prev, pager, next, jumper"
            :total="total"
            @current-change="handleCurrentChange"/>
      </div>
    </div>
  </div>
</template>
<script>
  export default {
    props: {
      table1Props: Object,
      table2Props: Object,
    }
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容