美文网首页
Vue使用$attrs与$listeners实现多层嵌套传递

Vue使用$attrs与$listeners实现多层嵌套传递

作者: liunewshine | 来源:发表于2019-09-29 17:17 被阅读0次

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

    现有3个嵌套组件,A->B,B->C。
    现在我么需要在A中对C的props赋值,监听C的emit事件

    图片

    A组件与C组件通信,我们有多少种解决方案?

    1. 我们使用vuex来进行数据管理,依赖于vuex我们可以一次改变,任何一个组件中都能获取。但是如果多个组件共享状态比较少,使用vuex过于麻烦和难以维护。element-ui中大量采用此方法。
    2. 自定义vue bus事件总线,原理类似vuex,使用特别简单。bus适合碰到组件跨级兄弟组件等无明显依赖关系的消息传递,原生app开发中经常用到,但是缺点是bus破坏了代码的链式调用,大量的滥用将导致逻辑的分散,出现问题后很难定位,降低了代码可读性。
    3. 使用B来做中转,A传递给B,B再给C这是最容易想到的方案,但是如果嵌套的组件过多,需要传递的事件和属性较多,会导致代码繁琐,代码维护困难。

    对于我们这种情况,3种方式都有局限性

    在vue2.4中,为了解决该需求,引入了$attrs$listeners,新增了inheritAttrs选项。我们只需要在B组件中对引入的C组件增加下面两个属性即可绑定所有的属性和事件。

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

    A组件

    <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组件

    <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中传递过来的属性
        console.info(this.$attrs, this.$listeners);
      },
      methods: {
        hInput() {
          this.$emit("changeMyData", this.myc); // // 在组件A中传递过来的事件
        }
      }
    };
    </script>
    

    实际应用

    element-ui开发的后台项目中,大量使用到了el-tableel-pagination做分页数据展示,所以我封装一个自定义组件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的地方无法使用el-table属性和事件。我们在page-table中把所有el-table的属性和事件都中转一遍?有上百个呢。

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

    相关文章

      网友评论

          本文标题:Vue使用$attrs与$listeners实现多层嵌套传递

          本文链接:https://www.haomeiwen.com/subject/wrfqpctx.html