详解vue中$attrs和$listeners在祖孙通信中的用法

248 阅读1分钟

首先看在vue官方文档中,对$attrs$listeners的定义

image.png

image.png 感觉有点难理解,那把它放在实例中看下吧

现有一个场景,有以下的目录结构

1658238178955.png

正常情况下,在爷组件中的参数,传到父组件,父组件用props进行接收并可以使用,父组件可以继续传给孙组件,孙组件用props进行接收并可以使用,那如果父组件不需要用这个参数,只有孙组件中需要使用,那父组件中写props就显得多余了。

$attrs的用法

先看实例

<!-- desc:功能描述 -->
<template>
  <div class="page">
    <span>爷页面</span>
    <Father :name="name" :gender="gender" :age="age" />
  </div>
</template>

<script>
import Father from './component/father.vue'

export default {
  name: 'Grandpa',
  components: {
    Father
  },
  data () {
    return {
      name: '张三',
      gender: '男',
      age: 18,
    }
  }
}
</script>

在爷组件中,会把三个值传给父组件,这时父组件只需要使用其中一个参数,便用props接收

<template>
  <div class="father">
    <span>父组件</span>
    <div>我儿子的名字是{{ name }}</div>
    <Sun :name="name"/>
  </div>
</template>
<script>
import Sun from './sun.vue'
export default {
  components: {
    Sun
  },
  props: {
    name: { type: String, default: '' }
  },
  data () {
    return {

    }
  }
}
</script>

孙组件也是

<!-- desc:功能描述 -->
<template>
  <div class="sun">
    <span>孙组件</span>
    <div>我的名字是{{ name }}</div>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    name: { type: String, default: '' }
  },
  data () {
    return {}
  },
}
</script>

现在是正常用props接收,能回显数据

image.png

那$attrs有什么用呢?我们让他显示出来 在父组件中加入

<div>$attrs里的内容是:{{ $attrs }}</div>

image.png

从页面上可以看到,$attrs里的内容是上个组件传过来的,并且未被props接收的

看到这里就能解决我们刚开始的场景了,爷组件将数据传给父组件,父组件并不使用,就不需要用props接收,那么我们就可以用v-bind='$attrs'传给孙组件,孙组件再用props接收就可以了

父组件中

<template>
  <div class="father">
    <span>父组件</span>
    <div>我儿子的名字是{{ name }}</div>
    <div>$attrs里的内容是:{{ $attrs }}</div>
    <Sun :name="name" v-bind="$attrs" />
  </div>
</template>

孙组件中

<!-- desc:功能描述 -->
<template>
  <div class="sun">
    <span>孙组件</span>
    <div>我的名字是: {{ name }}</div>
    <div>我的性别是: {{ gender }}</div>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    name: { type: String, default: '' },
    gender: { type: String, default: '' }
  },
  data () {
    return {}
  },
}
</script>

当然孙组件中也可以通过$attrs.gender去获取到 最终实现祖传孙,如图效果

image.png

$listeners的用法

$listeners 是实现孙组件的数据,传到爷组件中,类似于$attrs, 在父组件中加入 v-on="$listeners"

<template>
 <div class="father">
   <span>父组件</span>
   <div>我儿子的名字是{{ name }}</div>
   <div>$attrs里的内容是:{{ $attrs }}</div>
   <Sun :name="name" v-bind="$attrs" v-on="$listeners" />
 </div>
</template>

孙组件发送数据

<template>
 <div class="sun">
   <span>孙组件</span>
   <div>我的名字是: {{ name }}</div>
   <div>我的性别是: {{ gender }}</div>
   <el-button type="primary" @click="sendInfo">点击向爷组件传递数据</el-button>
 </div>
</template>

<script>
export default {
 name: '',
 inheritAttrs: false, // 默认会继承在html标签上传递过来的数据,类似href属性的继承
 props: {
   name: { type: String, default: '' },
   gender: { type: String, default: '' }
 },
 data () {
   return {}
 },

 // 组件方法
 methods: {
   sendInfo () {
     this.$emit('getSun', '这是从孙组件中获得的数据')
   }
 }
}
</script>

爷组件中接收

<template>
 <div class="page">
   <span>爷页面</span>
   <Father :name="name" :gender="gender" :age="age" @getSun="fromSun" />
   <div>从孙组件传来的数据: {{ sunInfo }}</div>
 </div>
</template>

<script>
import Father from './component/father.vue'

export default {
 name: 'Grandpa',
 components: {
   Father
 },
 data () {
   return {
     name: '张三',
     gender: '男',
     age: 18,
     sunInfo: ''
   }
 },

 // 组件方法
 methods: {
   fromSun (data) {
     console.log(data, 111)
     this.sunInfo = data
   }
 }
}
</script>

最终效果,点击孙组件中的按钮,传递数据,在爷组件中能收到

image.png

顺带有一个属性interitAttrs

  $attrs一般搭配interitAttrs 一块使用
  inheritAttrs一般为false
  默认会继承在html标签上传递过来的数据,类似href属性的继承

总结一下,$attrs$linteners都相当于一个桥梁,接通了整个数据的传递, 这在组件的二次封装,或是对深层的组件嵌套来说帮助比较大的,使代码看起来更简洁,再回去看官方的解释就比较好理解啦!