$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 小时前
Vibe coding工具使用心得
前端·ai编程
brien98 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance8 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M8 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写8 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李8 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易9 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马9 小时前
前沿视界
前端
kyriewen9 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易9 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员