$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"解决事件透传问题

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

相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排7 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60617 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角8 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!8 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰9 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen11 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马11 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒12 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端