$attr/$listeners的使用场景 — vue组件通信系列

时间:2021-2-20 作者:admin

vue组件的数据通信方式很多,本篇着重讲$attr/$listeners,神助是v-model/.sync语法糖。

$attr/$listeners的常用场景:封装第三方组件或者表单组件,从而减少组件处理成本。

$attrs

使用组件的时候,vue内部会将组件上面的属性,自动会合并到组件内部根元素上面。

<template lang="pug">
  list-item(a="a" b="b" c="c" style="color:red")
</template>
<!-- ListItem组件 -->
<template lang="pug">
  section(title="hello") 我是组件内部
</template>

渲染的时候会这样

<section title="hi" a="a" b="b" c="c">组件内部</section>

但这里限制在组件根元素上,若,组件内部其他元素也需要使用属性呢,此时就用到$attrs。

<template lang="pug">
section(title="hi") 组件内部
  //- { "a": "a", "b": "b", "c": "c" }
  div {{$attrs}}
</template>

这里如果不希望根元素继承那些属性,可以在组件内部配置inheritAttrs: false

$listeners

同理,对于事件,如果组件上面的事件是native模式,组件内部的最外层元素也会自动绑定该事件。

<template lang="pug">
  list-item(@click.native="()=>{ title = 1}")
</template>
<!-- ListItem组件 -->
<template lang="pug">
  section 我是组件内部
</template>

渲染的时候,事件就会自动绑定到最外层元素上面

<section @click="()=>{ title = 1}">组件内部</section>

但这里任然限制在组件根元素上,若,组件内部其他元素也需要绑定事件呢,此时就用到$listeners,其是非native事件的合集。

<template lang="pug">
//- 父组件
div
  span {{title}}
  list-item(
    @click="()=>{ title = '点了 内部绑定事件的元素'}"
    @mousedown="()=>{ title = 'mousedown'}"
    @click.native="()=>{ title='点了 内部根元素'}")
</template>
<script>
import ListItem from "@/components/ListItem";
export default {
  name: "List",
  components: { ListItem },
  data() {
    return {
      title: "点击组件的不同元素修改标题"
    };
  }
};
</script>

<template lang="pug">
section(title="hi") 组件根元素
  div(@click.stop="$listeners.click") 组件绑定事件的元素
</template>
<script>
export default {
  name: "ListItem",
  mounted() {
    // {click:fn,mousedown:fn}
    console.log("$listeners", this.$listeners);
  }
};
</script>

使用场景:封装第三方组件、表单组件

封装第三方组件、或者表单组件的的时候使用,往往结合v-bind="$attrs"/v-on="$listeners",这样就不用考虑用户给组件传入什么属性或者事件了。

比如希望封装baidu-map组件,而baidu-map上面的属性和事件直接用他们自己库的。

<template lang="pug">
  enforced-map(:title="title" @resizeMap="...")
</template>
<!-- EnforcedMap.vue  -->
<template lang="pug">
div
  baidu-map(v-bind="$attrs" v-on="$listeners")
</template>

封装表单组件的时候,inputListeners之类的计算属性通常非常有用。
始终牢记,子组件想要改变父组件的数据,需要$emit哈。

<template lang="pug">
//- 父组件
div
  enhanced-input(v-model="title" label='标题')
  div {{title}}
</template>
<script>
import EnhancedInput from "@/components/ListItem";
export default {
  name: "List",
  components: { EnhancedInput },
  data() {
    return {
      title: "初始值"
    };
  }
};
</script>

组件内部如下:

<template lang="pug">
label {{label}}
  //- 这里记得绑定value的值,这样才能显示初始值
  input(v-bind="$attrs",:value="value",v-on="inputListeners")
</template>
<script>
export default {
  name: "enhanced-input",
  inheritAttrs: false,
  props: ["label", "value"],
  mounted() {},
  computed: {
    inputListeners() {
      const newListeners = {
        // 我们从父级添加所有的监听器
        ...this.$listeners,
        // 然后我们添加自定义监听器,或覆写一些监听器的行为
        // 这里确保组件配合 `v-model` 的工作
        input: event => this.$emit("input", event.target.value)
      };
      return newListeners;
    }
  }
};
</script>

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。