【组件二次封装必会技巧】$attrs组件传值

504 阅读3分钟

前言

在日常开发中,如果遇到当前提供的组件不足以完成现在的功能,但是当前的组件已经有完整地代码或者是UI组件库 (elementUI、vantUI) 提供的成套组件,使我们不能对源码进行修改,那就要二次封装或许是一个很好的方案!!!

什么是二次封装

androidhierchy.jpg

我们可以把children组件当做最底层的组件,但是children组件不能满足最新的需求,那么在允许不修改children组件的前提下,就可以在children组件外面套一层parent组件,那么这就是组件的二次封装。

预热尝鲜

首先用最简单的方式实现一下上面图的功能!!

 // root组件
 <template>
   <parent :a="a" b="b" :c="c"></parent>
 </template>
 ​
 <script>
 import parent from "./parent.vue";
 export default {
   components: { parent },
   data() {
     return {
       a: 1,
       b: 2,
       c: 3,
     };
   },
 };
 </script>
 
 // parent组件
 <template>
   <div>
     <h3>我是来自root组件的数据a==>{{ a }}</h3>
     <children b="b" :c="c"></children>
   </div>
 </template>
 ​
 <script>
 import children from "./children.vue";
 export default {
   components: { children },
   props: ["a", "b", "c"],
 };
 </script>
 // children组件
 <template>
   <div>
     <h5>我是来自root组件的数据b==>{{ b }}</h5>
     <h5>我是来自root组件的数据c==>{{ c }}</h5>
   </div>
 </template>
 ​
 <script>
 export default {
   props: ["b", "c"],
 };
 </script>

上面的代码理论上实现了二次封装的效果,如果以后还需children组件进行扩充,完全可以在parent组件上面进行修改。

parent组件组件会接受来自root组件的所以数据,做一个中转站传递给children组件,这里明显可以发现虽然parent组件没有使用bc,但还是需要在props接受,这数据量小还是可以接受的。

但是这里提出一个问题,如果children组件elementUI的内置组件,在开发的过程中根本不知道children组件所需的参数喃?有的人就会说喃,怎么可能不知道喃,完全可以通过查看文档和源码来确定呀,那如果children组件需要100多个参数,我们也要去用parent组件做为数据的中转吗?想想是不是很繁琐荒唐。

$attrs的妙用

$attrs到底是什么

首先看一下来自官方的八股文

 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

还是使用上面的代码例子,但是要稍微改动一下parent组件

 // root组件
 <template>
   <parent :a="a" b="b" :c="c"></parent>
 </template>
 ​
 <script>
 import parent from "./parent.vue";
 export default {
   components: { parent },
   data() {
     return {
       a: 1,
       b: 2,
       c: 3,
     };
   },
 };
 </script>
 <template>
   <div>
     <h3>我是来自root组件的数据a==>{{ a }}</h3>
     <h1>我是parent组件的$attrs==>{{ $attrs }}</h1>
   </div>
 </template>
 ​
 <script>
 export default {
   props: ["a"], //props只接受本组件需要的参数
 };
 </script>

image-20221005122825808.png 可以发现parent组件改成了props只接受本组件需要的参数;打印$attrs发现输出了一个对象,对象键值对包含了parent组件的props并没有接受的bc参数,那这里也可以简单的理解一下;$attrs值就是一个对象,里面的键值对就是当前组件props并没有接受的参数,当一个组件没有声明任何 prop 时,$attrs就会包含所有父级组件传递的参数 (class 和 style 除外),现在再去结合官网的八股文,大概就能猜到什么意思了吧。

v-bind的特殊使用

这里就简单补充一下这个知识,还是使用上面的例子

 // root组件
 <template>
   <!-- <parent :a="a" b="b" :c="c"></parent> -->
   <parent v-bind="{ a: a, b: b, c: c }"></parent> 
 </template>
 ​
 <script>
 import parent from "./parent.vue";
 export default {
   components: { parent },
   data() {
     return {
       a: 1,
       b: 2,
       c: 3,
     };
   },
 };
 </script>

root组件通过v-bind传值所达到的效果和attribute是一样,区别就是v-bind接受可以一个对象,并且把对象拆除单独的参数去传递给子组件

使用$attrs实现二次封装

学习了上面的知识,那么我就可以如果$attrs配合v-bind来实现更加完善的组件二次封装

 
 // root组件
 <template>
   <parent :a="a" b="b" :c="c"></parent>
 </template>
 ​
 <script>
 import parent from "./parent.vue";
 export default {
   components: { parent },
   data() {
     return {
       a: 1,
       b: 2,
       c: 3,
     };
   },
 };
 </script>
 
 // parent组件
 <template>
   <div>
     <h3>我是来自root组件的数据a==>{{ a }}</h3>
     <children v-bind="$atts"></children>
   </div>
 </template>
 ​
 <script>
 import children from "./children.vue";
 export default {
   components: { children },
   props: ["a"],
 };
 </script>
 // children组件
 <template>
   <div>
     <h5>我是来自root组件的数据b==>{{ b }}</h5>
     <h5>我是来自root组件的数据c==>{{ c }}</h5>
   </div>
 </template>
 ​
 <script>
 export default {
   props: ["b", "c"],
 };
 </script>

只需改动parent组件就可以拉,回顾一下我们采用第一种方案,如果children组件需要参数,还需要parent组件进行传递,但是在使用$attrs就完全不用考虑这些问题了,我们可以在root组件传递,children组件可以直接接收到,完全不用parent组件进行传递

结语

其实除了$attrsv-bind配合传值外,还有$listenersv-on传递事件函数,两者原理和解决的问题也是相似的