$attrs和v-on="$listenter"

一般在项目中,我们直接使用第三方库,比如element-ui,或者是一些其它的库,这些库中早就写好了他们自己有的组件。
在实际开发项目中,我们可能需要根据业务来进行对第三方库组件的封装,从而实现一个符合自己业务的封装组件,本质上而言就是进行组件的二次封装。
对于组件的二次封装,我们需要了解属性透传的概念,大概意思就是我们调用的组件传递的属性直接传递到第三方库组件中,数据流的方向是这样的:前端调用传递属性数据->已经二次封装的组件的属性数据->第三方原生组件属性数据

$attrs

这个属性在二次封装组件中非常有用,表示的是父组件传递给子组件的属性,但是传递的属性没有被子组件显示声明在props中。

xml 复制代码
//ParentComponent
<template>
  <child-component message="Hello from parent" custom-attribute="customValue"></child-component>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent,
  },
};
</script>
xml 复制代码
//ChildComponent
<template>
  <div>
    <p>{{ message }}</p>
    <p>{{ customAttribute }}</p>
  </div>
</template>

<script>
export default {
  props: {
    message: String,
  },
  created() {
    console.log(this.$attrs); // 输出:{ custom-attribute: "customValue" }
  },
};
</script>

在这个例子中,ChildComponent 中的 message 是一个通过 props 声明的属性,而 custom-attribute 则是没有在 props 中声明的属性。当 ParentComponent 传递这两个属性给 ChildComponent 时,custom-attribute 会被自动合并到 $attrs 对象中。

inheritAttrs

inheritAttrs在父子组件中也是非常常用的,默认值未true,即子组件继承父组件传递的未声明为 props 的属性,比如上面的 :

ini 复制代码
<child-component message="Hello from parent" 
custom-attribute="customValue"></child-component>

如果子组件中props没有进行显示的声明custom-attribute属性,如果inheritAttrs为false,那么子组件的根部元素是不会继承这个属性的,反之则会继承。

如果你设置了 inheritAttrs: false,那么默认情况下,Vue 不会将父组件传递的未声明为 props 的属性合并到子组件的根元素上。

当你使用 inheritAttrs: true 时,Vue 会将所有未在子组件中声明为 props 的属性自动应用到子组件的根元素上。这就意味着你在子组件内部直接可以使用这些属性,而不需要额外使用 $attrs

xml 复制代码
<!-- ParentComponent.vue -->
<template>
  <div>
    <child-component custom-attribute="customValue" message="Hello from parent"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent,
  },
};
</script>
xml 复制代码
<!-- ChildComponent.vue -->
<template>
  <div>
    <p>{{ message }}</p>
    <p>{{ customAttribute }}</p>
  </div>
</template>

<script>
export default {
  props: {
    message: String,
  },
  created() {
    // 因为 inheritAttrs: true,custom-attribute 会直接应用到子组件的根元素上
    console.log(this.$attrs); // 输出:{ custom-attribute: "customValue" }
  },
};
</script>

如果 inheritAttrs 被设置为 false,那么未在 props 中声明的属性将不会自动绑定到子组件的根元素上。因此,你将无法直接在子组件的模板中使用 {{ customAttribute }}

xml 复制代码
<!-- ChildComponent.vue -->
<template>
  <div>
    <p>{{ message }}</p>
    <p>{{ $attrs['custom-attribute'] }}</p>
  </div>
</template>

<script>
export default {
    inheritAttrs: false,
  props: {
    message: String,
  },
  created() {
    console.log(this.$attrs); // 输出:{ custom-attribute: "customValue" }
  },
};
</script>
v-on="$listeners"

Vue.js 中的一个语法糖,用于将父组件传递给子组件的所有事件监听器绑定到子组件的根元素上。本质上是为了代码的美观,代码更加易读易写,本身不会引入新的功能。
这样做的目的是为了方便地将父组件中的事件监听器应用到子组件上,而无需在子组件中显式声明这些事件。

比如下面显示声明:

xml 复制代码
<!-- ParentComponent.vue -->
<template>
  <div>
    <child-component @custom-event="handleCustomEvent"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent,
  },
  methods: {
    handleCustomEvent() {
      console.log('Custom event handled in parent component');
    },
  },
};
</script>

需要专门写一个事件来触发emit
而如果使用了这种语法糖,那么:

xml 复制代码
<!-- ChildComponent.vue -->
<template>
  <div @custom-event="handleCustomEvent">
    <button @click="$emit('custom-event')">Trigger Custom Event</button>
  </div>
</template>

<script>
export default {
  methods: {
    handleCustomEvent() {
      console.log('Custom event handled in child component');
    },
  },
};
</script>

是不是感觉到了v-on="listeners"的一部分用处?上面的示例只是两个组件之间的调用,所以还不能很好的体现它的好处,当在三个组件,甚至四个,五个或者更多组件之间进行事件监听响应以及传递的时候,它的好处就显示出来了,比如下面这个示例: 使用v-on="listeners"示例:

xml 复制代码
<!-- 爷父子三个组件 -->
<!-- GrandparentComponent.vue -->
<template>
  <div>
    <parent-component @custom-event="handleCustomEvent"></parent-component>
  </div>
</template>

<script>
import ParentComponent from './ParentComponent.vue';

export default {
  components: {
    ParentComponent,
  },
  methods: {
    handleCustomEvent() {
      // 处理来自父组件的事件
    },
  },
};
</script>

<!-- ParentComponent.vue -->
<template>
  <div>
    <child-component v-on="$listeners"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent,
  },
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <!-- 透传父组件的事件到子组件 -->
    <grandchild-component v-on="$listeners"></grandchild-component>
  </div>
</template>

<script>
import GrandchildComponent from './GrandchildComponent.vue';

export default {
  components: {
    GrandchildComponent,
  },
};
</script>

<!-- GrandchildComponent.vue -->
<template>
  <div @click="$emit('custom-event')">Click me</div>
</template>

<!-- ... -->

不使用

xml 复制代码
<!-- GrandparentComponent.vue -->
<template>
  <div>
    <ParentComponent @custom-event="handleCustomEvent"></ParentComponent>
  </div>
</template>

<script>
import ParentComponent from './ParentComponent.vue';

export default {
  components: {
    ParentComponent,
  },
  methods: {
    handleCustomEvent() {
      // 处理来自父组件的事件
    },
  },
};
</script>

<!-- ParentComponent.vue -->
<template>
  <div>
    <ChildComponent @custom-event="handleCustomEvent"></ChildComponent>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent,
  },
  methods: {
    handleCustomEvent() {
      // 透传事件给父组件
      this.$emit('custom-event');
    },
  },
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <GrandchildComponent @custom-event="handleCustomEvent"></GrandchildComponent>
  </div>
</template>

<script>
import GrandchildComponent from './GrandchildComponent.vue';

export default {
  components: {
    GrandchildComponent,
  },
  methods: {
    handleCustomEvent() {
      // 透传事件给父组件
      this.$emit('custom-event');
    },
  },
};
</script>

<!-- GrandchildComponent.vue -->
<template>
  <div @click="handleClick">Click me</div>
</template>

<script>
export default {
  methods: {
    handleClick() {
      // 透传事件给父组件
      this.$emit('custom-event');
    },
  },
};
</script>

非常一个明显的特点就是当涉及两个组件以上的事件监听和传递时,我们可以发现使用v-on="listeners"只需要在中间组件上写上这个语法糖即可,而对于不使用这个语法糖的组件,对于中间的组件必须要一直进行同一个事件的监听重复编写.
所以这也是为什么使用语法糖可以解决代码美观问题。而对于第三方组件的封装,因为我们需要对事件保持原有的名字,所以直接使用v-on="$listenter"解决事件透传问题

这其实也算是一种中间商的设计方式吧。

相关推荐
这我可不懂几秒前
低代码开发 实战转型案例一览
前端·低代码·程序员
明月看潮生7 分钟前
青少年编程与数学 02-005 移动Web编程基础 06课题、响应式设计
前端·青少年编程·编程与数学·移动web
明月看潮生8 分钟前
青少年编程与数学 02-005 移动Web编程基础 07课题、多媒体形式
前端·青少年编程·移动开发·编程与数学·移动web
べJL13 分钟前
SVG怎么画渐变甜甜圈(进度环)
前端·css
初遇你时动了情13 分钟前
css3滚动边框特效属性 filter、inset应用
前端·css·css3
Ares码农人生38 分钟前
React 前端框架简介
前端·react.js·前端框架
小汤猿人类39 分钟前
nacos-gateway动态路由
java·前端·gateway
GISer_Jing39 分钟前
前端经典面试合集(二)——Vue/React/Node/工程化工具/计算机网络
前端·vue.js·react.js·node.js
GesLuck1 小时前
C#控件开发4—仪表盘
前端·经验分享·c#
小林爱2 小时前
【Compose multiplatform教程14】【组件】LazyColumn组件
android·前端·kotlin·android studio·框架·多平台