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

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

相关推荐
吕彬-前端14 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱16 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai26 分钟前
uniapp
前端·javascript·vue.js·uni-app
bysking1 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端
齐 飞2 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb