Vue使用$attrs與$listeners實現多層嵌套傳遞

官方文檔中的解釋 cn.vuejs.org/v2/guide/co…html

現有3個嵌套組件,A->B,B->C。 如今我麼須要在A中對C的props賦值,監聽C的emit事件 vue

圖片

A組件與C組件通訊,咱們有多少種解決方案?vuex

  1. 咱們使用vuex來進行數據管理,依賴於vuex咱們能夠一次改變,任何一個組件中都能獲取。可是若是多個組件共享狀態比較少,使用vuex過於麻煩和難以維護。element-ui中大量採用此方法。
  2. 自定義vue bus事件總線,原理相似vuex,使用特別簡單。bus適合碰到組件跨級兄弟組件等無明顯依賴關係的消息傳遞,原生app開發中常常用到,可是缺點是bus破壞了代碼的鏈式調用,大量的濫用將致使邏輯的分散,出現問題後很難定位,下降了代碼可讀性。
  3. 使用B來作中轉,A傳遞給B,B再給C**,**這是最容易想到的方案,可是若是嵌套的組件過多,須要傳遞的事件和屬性較多,會致使代碼繁瑣,代碼維護困難。

對於咱們這種狀況,3種方式都有侷限性element-ui

在vue2.4中,爲了解決該需求,引入了$attrs$listeners,新增了inheritAttrs選項。咱們只須要在B組件中對引入的C組件增長下面兩個屬性便可綁定全部的屬性和事件。bash

<C v-bind="$attrs" v-on="$listeners"></C>
複製代碼

A組件app

<template>
<div>
  <h2>組件A 數據項:{{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組件ide

<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組件ui

<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中傳遞過來的屬性
    console.info(this.$attrs, this.$listeners);
  },
  methods: {
    hInput() {
      this.$emit("changeMyData", this.myc); // // 在組件A中傳遞過來的事件
    }
  }
};
</script>
複製代碼

實際應用

element-ui開發的後臺項目中,大量使用到了el-tableel-pagination作分頁數據展現,因此我封裝一個自定義組件page-tablethis

<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的地方沒法使用el-table屬性和事件。咱們在page-table中把全部el-table的屬性和事件都中轉一遍?有上百個呢。spa

只需在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>

複製代碼

咱們有時候會碰到有多個兄弟組件須要傳遞參數到最外層,若有B組件包含C1和C2,都須要和A交互,定義2個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>
複製代碼
相關文章
相關標籤/搜索