每日一题

121 阅读1分钟

2019/1/5

在vue中通过使用$attrs实现组件之间的数据传递

组件之间传递数据的方式有很多种,之所以有这么多种方式,是为了满足在不同场景不同条件下的使用

三种常用组件传递值的方式

  • 通过props的方式向子组件传递(父子传递)
  • vuex 进行状态管理
  • 非父子组件的通信传递Vue Event Bus,使用Vue的实例,实现事件的监听和发布,实现组件之间的传递

$attrs的概念

包含了父作用域中不作为prop被识别(且获取)的特性绑定(class和style除外)。 当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外)。 并且可以通过v-bind=“$attrs” 传入内部组件-在创建高级别的组件时非常有用

通俗点讲:$attrs可以手机父组件中的所有传过来的属性除了那些在组建中没有通过props定义的。

引申说明一下,如果组件嵌套层级有点深但是又不那么深,比如三层。 如果我们使用props的话,最里面的组件想要获取最外层组件的数据,就是通过中间的组件的props来传递,但是这个props对于中间的这个组件没啥用处,就是做了一个桥梁而已。平常写代码的时候经常遇到。例如下面情景

// 主组件
<template>
  <div>
    <father :name='name'
            :sex="sex"
            :age="age"></father>
  </div>
</template>
<script>
import father from "./father.vue"
export default {
  name: 'test',
  components: {
    father
  },
  data () {
    return {
      name: '测试',
      sex: '男',
      age: 18,
    }
  }
}
</script>

//father 组件
<template>
  <div>
    <ul>
      <li>姓名:{{name}}</li>
      <!-- <child v-bind="$attrs"></child> -->
      <child :sex="sex"></child>
      <li>年龄:{{age}}</li>
    </ul>
  </div>
</template>
<script>
import child from "./child.vue"
export default {
  name: 'father',
  components: {
    child
  },
  data () {
    return {
      newAge: this.age
    }
  },
  props: {
    name: {
      type: String,
      default: '',
    },
    sex: {
      type: String,
      default: ''
    },
    age: {
      type: [Number, String],
      default: 0,
    }
  },


}
</script>

//child组件
<template>
  <div>
    <h2>性别:{{sex}}</h2>
  </div>
</template>
<script>
export default {
  name: 'child',
  props: {
    sex: {
      type: String,
      default: '',
    }
  },
  data () {
    return {
      newSex: this.sex,
    }
  },
}
</script>

虽然这种方式也能实现我们想要的效果,如果传的值少的话,其实也没啥影响,但是如果传的值很多,就会影响代码,这个时候就可以使用$attrs这种api,大大解决了我们的问题。

$attrs具体的使用方式

//主组件
<template>
  <div>
    <father :name='name'
            :sex="sex"
            :age="age"></father>
  </div>
</template>
<script>
import father from "./father.vue"
export default {
  name: 'test',
  components: {
    father
  },
  data () {
    return {
      name: '测试',
      sex: '男',
      age: 18,
    }
  }
}
</script>

//father组件
<template>
  <div>
    <ul>
      <li>姓名:{{name}}</li>
      <child v-bind="$attrs"></child>
      <!-- <child :sex="sex"></child> -->
      <li>年龄:{{age}}</li>
    </ul>
  </div>
</template>
<script>
import child from "./child.vue"
export default {
  name: 'father',
  components: {
    child
  },
  data () {
    return {
      newAge: this.age
    }
  },
  props: {
    name: {
      type: String,
      default: '',
    },
    age: {
      type: [Number, String],
      default: 0,
    }
  },


}
</script>

//child组件
<template>
  <div>
    <h2>性别:{{sex}}</h2>
  </div>
</template>
<script>
export default {
  name: 'child',
  props: {
    sex: {
      type: String,
      default: '',
    }
  },
  data () {
    return {
      newSex: this.sex,
    }
  },

  methods: {

  },
}
</script>

注意当子组件使用attrs时,中间组件不能使用props定义这个值,只有子组件props才能定义使用,但是如果使用async,在子组件中使用this.attrs时,中间组件不能使用props定义这个值,只有子组件props才能定义使用,但是如果使用async,在子组件中使用this.emit("update:xx",xx),是没有作用的

当孙子组件传递给信息给爷爷组件时就需要使用$listeners

$listeners 概念

包含了父作用域中的(不含.native修饰器)v-on事件监听器。它可以通过v-on="$listeners" 传入内部组件--在创建更高层次的组件时非常有用 (孙传父专用),解觉子传父这个功能。

示例代码

//主组件
<template>
  <div>
    <father :name='name'
            :sex="sex"
            :age="age"
            @getChildSex="getChildSex"></father>
  </div>
</template>
<script>
import father from "./father.vue"
export default {
  name: 'test',
  components: {
    father
  },
  data () {
    return {
      name: '测试',
      sex: '男',
      age: 18,
    }
  },
  methods: {
    getChildSex (val) {
      this.sex = val;
      console.log(val);
    }
  }
}
</script>

//father组件
<template>
  <div>
    <ul>
      <li>姓名:{{name}}</li>
      <child v-bind="$attrs"
             v-on="$listeners"></child>
      <!-- <child :sex="sex"></child> -->
      <li>年龄:{{age}}</li>
    </ul>
  </div>
</template>
<script>
import child from "./child.vue"
export default {
  name: 'father',
  components: {
    child
  },
  data () {
    return {
      newAge: this.age
    }
  },
  props: {
    name: {
      type: String,
      default: '',
    },
    age: {
      type: [Number, String],
      default: 0,
    }
  },


}
</script>

//child组件
<template>
  <div>
    <h2>性别:{{sex}}</h2>
    <button @click="changeSex">切换性别</button>
  </div>
</template>
<script>
export default {
  name: 'child',
  props: {
    sex: {
      type: [String, Number],
      default: '',
    }
  },
  data () {
    return {
      newSex: this.sex,
    }
  },

  methods: {
    changeSex () {
      this.newSex = this.sex;

      this.$emit('getChildSex', this.sex == '男' ? '女' : '男');
    }
  },
}
</script>

其实也可以这么理解v-bind="attrs"von="attrs"和v-on="listeners" 两者结合使用的,一个是传值给孙组件,一个是孙组件通知主组件进行数据传递。形成一个完整的流程。

可以用一张图分析观看