title: ‘Vue inheritAttrs, Vue-inheritAttrs-attrs-listeners-详解 - 图1listeners 详解’
author: Tomatoro
comments: true
tags:

  • Vue
    top: 0
    abbrlink: d95d5814
    date: 2019-12-30 10:00:55

1、vm.$attrs简介

首先我们来看下vue官方对vm.Vue-inheritAttrs-attrs-listeners-详解 - 图2%20%E7%9A%84%E7%89%B9%E6%80%A7%E7%BB%91%E5%AE%9A%20(class%20%E5%92%8C%20style%20%E9%99%A4%E5%A4%96)%E3%80%82%E5%BD%93%E4%B8%80%E4%B8%AA%E7%BB%84%E4%BB%B6%E6%B2%A1%E6%9C%89%E5%A3%B0%E6%98%8E%E4%BB%BB%E4%BD%95%20prop%20%E6%97%B6%EF%BC%8C%E8%BF%99%E9%87%8C%E4%BC%9A%E5%8C%85%E5%90%AB%E6%89%80%E6%9C%89%E7%88%B6%E4%BD%9C%E7%94%A8%E5%9F%9F%E7%9A%84%E7%BB%91%E5%AE%9A%20(class%20%E5%92%8C%20style%20%E9%99%A4%E5%A4%96)%EF%BC%8C%E5%B9%B6%E4%B8%94%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%20v-bind%3D%22#card=math&code=attrs%E7%9A%84%E4%BB%8B%E7%BB%8D%EF%BC%9A%20%E5%8C%85%E5%90%AB%E4%BA%86%E7%88%B6%E4%BD%9C%E7%94%A8%E5%9F%9F%E4%B8%AD%E4%B8%8D%E4%BD%9C%E4%B8%BA%20prop%20%E8%A2%AB%E8%AF%86%E5%88%AB%20%28%E4%B8%94%E8%8E%B7%E5%8F%96%29%20%E7%9A%84%E7%89%B9%E6%80%A7%E7%BB%91%E5%AE%9A%20%28class%20%E5%92%8C%20style%20%E9%99%A4%E5%A4%96%29%E3%80%82%E5%BD%93%E4%B8%80%E4%B8%AA%E7%BB%84%E4%BB%B6%E6%B2%A1%E6%9C%89%E5%A3%B0%E6%98%8E%E4%BB%BB%E4%BD%95%20prop%20%E6%97%B6%EF%BC%8C%E8%BF%99%E9%87%8C%E4%BC%9A%E5%8C%85%E5%90%AB%E6%89%80%E6%9C%89%E7%88%B6%E4%BD%9C%E7%94%A8%E5%9F%9F%E7%9A%84%E7%BB%91%E5%AE%9A%20%28class%20%E5%92%8C%20style%20%E9%99%A4%E5%A4%96%29%EF%BC%8C%E5%B9%B6%E4%B8%94%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%20v-bind%3D%22)attrs” 传入内部组件——在创建更高层次的组件时非常有用。 猛一看有点看不明白….

2、场景介绍

vue中一个比较令人烦恼的事情是属性只能从父组件传递给子组件。这也就意味着当你想向嵌套层级比较深组件数据传递,只能由父组件传递给子组件,子组件再传递给孙子组件…像下面这样:

  1. <parent-component :passdown="passdown">
  2. <child-component :passdown="passdown">
  3. <grand-child-component :passdown="passdown">
  4. ....
  1. 就这样一层一层的往下传递passdown这个变量,最后才能用{{passdown}}。

假如我们需要传递的属性只有1,2个还行,但是如果我们要传递的有几个或者10来个的情况,这会是什么样的场景,我们会在每个组件不停的props,每个必须写很多遍。有没有其它方便的写法?有,通过vuex的父子组件通信,的确这个是一个方法,但是还有其它的方法,这个就是我们要说的。通过inheritAttrs选项,以及实例属性$attrs

3、实例:

  1. <template>
  2. <div class="home">
  3. <mytest :title="title" :massgae="massgae"></mytest>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: 'home',
  9. data () {
  10. return {
  11. title:'title1111',
  12. massgae:'message111'
  13. }
  14. },
  15. components:{
  16. 'mytest':{
  17. template:`<div>这是个h1标题{{title}}</div>`,
  18. props:['title'],
  19. data(){
  20. return{
  21. mag:'111'
  22. }
  23. },
  24. created:function(){
  25. console.log(this.$attrs)//注意这里
  26. }
  27. }
  28. }
  29. }
  30. </script>

上边的代码,我们在组件里只是用了title这个属性,massgae属性我么是没有用的,那么下浏览器渲染出来是什么样呢?如下图:

Vue-inheritAttrs-attrs-listeners-详解 - 图3

我们看到:组件内未被注册的属性将作为普通html元素属性被渲染,如果想让属性能够向下传递,即使prop组件没有被使用,你也需要在组件上注册。这样做会使组件预期功能变得模糊不清,同时也难以维护组件的DRY。在Vue2.4.0,可以在组件定义中添加inheritAttrs:false,组件将不会把未被注册的props呈现为普通的HTML属性。但是在组件里我们可以通过其$attrs可以获取到没有使用的注册属性,如果需要,我们在这也可以往下继续传递。

如果我们在子组件里设置 inheritAttrs: false:

  1. components:{
  2. 'mytest':{
  3. template:`<div>这是个h1标题{{title}}</div>`,
  4. props:['title'],
  5. inheritAttrs: false,
  6. data(){
  7. return{
  8. mag:'111'
  9. }
  10. },
  11. created:function(){
  12. console.log(this.$attrs)//注意这里
  13. }
  14. }

渲染效果如下:

Vue-inheritAttrs-attrs-listeners-详解 - 图4

补充:说一下$attrs的使用

有一个页面由父组件,子组件,孙子组件构成,如下:

  1. <template>
  2. <div style="padding:50px;">
  3. <childcom :name="name" :age="age" :sex="sex"></childcom>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. 'name':'test',
  9. props:[],
  10. data(){
  11. return {
  12. 'name':'张三',
  13. 'age':'30',
  14. 'sex':'男'
  15. }
  16. },
  17. components:{
  18. 'childcom':{
  19. template:`<div>
  20. <div>{{name}}</div>
  21. <grandcom v-bind="$attrs"></grandcom>
  22. </div>`,
  23. props:['name'],
  24. components: {
  25. 'grandcom':{
  26. template:`<div>{{$attrs}}</div>`,
  27. }
  28. }
  29. }
  30. }
  31. }
  32. </script>

上面的代码在页面的效果是如下图

Vue-inheritAttrs-attrs-listeners-详解 - 图5

如果attrs被绑定在子组件childcom上后,我们就可以在孙子组件grandcom里获取到this.Vue-inheritAttrs-attrs-listeners-详解 - 图6attrs}}的值是父组件中传递下来的props(除了子组件childcom组件中props声明的)。

记住孙子组件grandcom里获取到this.Vue-inheritAttrs-attrs-listeners-详解 - 图7attrs就不包含name属性,那么this.$attrs = { ‘age’:’30’, ‘sex’:’男’}。

补充:说一下$attrs的优势到底在哪

假如我们要做一个页面,有父组件,子组件,孙子组件,如下:

  1. <template>
  2. <div>
  3. <childcom></childcom>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. 'name':'test',
  9. props:[],
  10. data(){
  11. return {
  12. 'name':'张三',
  13. 'age':'30',
  14. 'sex':'男'
  15. }
  16. },
  17. components:{
  18. 'childcom':{
  19. template:`<div>
  20. <div>我是子组件</div>
  21. <grandcom></grandcom>
  22. </div>`,
  23. components: {
  24. 'grandcom':{
  25. template:`<div>我是孙子组件</div>`,
  26. }
  27. }
  28. }
  29. }
  30. }
  31. </script>

如上代码,假如我想在子组件想获取到父组件的name属性值,在孙子组件获取父组件的age属性值,用props的话就必须在父组件把name和age的值通过props传递到子组件,子组件在通过props把age的值传递到孙子组件,到这里看明白了吧,孙子组件需要的age在子组件里没有用到,但是为了能让孙子组件获取到,你必须从父组件 传到子组件,在在子组件传递到孙子组件。

但是用$attrs就不用那么麻烦,如下:

  1. <template>
  2. <div>
  3. <childcom :name="name" :age="age" :sex="sex"></childcom>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. 'name':'test',
  9. props:[],
  10. data(){
  11. return {
  12. 'name':'张三',
  13. 'age':'30',
  14. 'sex':'男'
  15. }
  16. },
  17. components:{
  18. 'childcom':{
  19. props:['name'],
  20. template:`<div>
  21. <div>我是子组件 {{name}}</div>
  22. <grandcom v-bind="$attrs"></grandcom>
  23. </div>`,
  24. components: {
  25. 'grandcom':{
  26. template:`<div>我是孙子组件{{$attrs.age}}</div>`,
  27. }
  28. }
  29. }
  30. }
  31. }
  32. </script>

子组件绑定了”$attrs”,孙子组件就能获取到除了name属性外所有由父组件传递下来的属性。如果孙子组件也想获取到name属性那么,在绑定个name如下,

  1. <grandcom v-bind="$attrs" :name="name"></grandcom>

细细体会下是不是这个道理。实在不行的话敲一敲代码自己试验下,你就会豁然开朗。

inheritAttrs属性

关于inheritAttrs这个属性跟获取到$attrs的值没有关系,inheritAttrs通常在编写基础组件时候会用到。官网原话:默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例属性 $attrs 可以让这些特性生效,且可以通过 v-bind 显性的绑定到非根元素上。

注意:这个选项不影响 class 和 style 绑定。

在Vue2.4.0之前版本,组件内未被注册的属性将作为普通html元素属性被渲染。

inheritAttrs到底有啥用?到底用在哪里?看下边代码,

  1. <template>
  2. <childcom :name="name" :age="age" type="text"></childcom>
  3. </template>
  4. <script>
  5. export default {
  6. 'name':'test',
  7. props:[],
  8. data(){
  9. return {
  10. 'name':'张三',
  11. 'age':'30',
  12. 'sex':'男'
  13. }
  14. },
  15. components:{
  16. 'childcom':{
  17. props:['name','age'],
  18. template:`<input type="number" style="border:1px solid blue">`,
  19. }
  20. }
  21. }
  22. </script>

上面代码你觉得input上会怎么显示? 父组件传递了type=”text”,子组件里input 上type=”number”,那渲染到页面会是什么样?渲染图如下:

Vue-inheritAttrs-attrs-listeners-详解 - 图8

看到没,父组件传递的type=”text”覆盖了input 上type=”number”,这岂不是把我的input数据类型都给改变了,这岂不是有问题,这不是我想要的!!!!看到这里明白了吗?回头去体会下上面官网的原话!!!

需求:我需要input 上type=”number”类型不变,但是我还是要取到父组件的type=”text”的值,那么代码如下:

  1. <template>
  2. <childcom :name="name" :age="age" type="text"></childcom>
  3. </template>
  4. <script>
  5. export default {
  6. 'name':'test',
  7. props:[],
  8. data(){
  9. return {
  10. 'name':'张三',
  11. 'age':'30',
  12. 'sex':'男'
  13. }
  14. },
  15. components:{
  16. 'childcom':{
  17. inheritAttrs:false,
  18. props:['name','age'],
  19. template:`<input type="number" style="border:1px solid blue">`,
  20. created () {
  21. console.log(this.$attrs.type)
  22. }
  23. }
  24. }
  25. }
  26. </script>

页面渲染图如下:

Vue-inheritAttrs-attrs-listeners-详解 - 图9

到这,我想大家都明白了inheritAttrs的作用了吧。默认情况下vue会把父作用域的不被认作 props 的特性绑定 且作为普通的 HTML 特性应用在子组件的根元素上。绑定就绑定,显示就显示,没啥大不了的,但是怕就怕遇到一些特殊的,就比如上面的input的情况,这个时候inheritAttrs:false的作用就出来啦。

$listeners

父组件-子组件-孙子组件,,,,现在我要你在孙子组件里改变父组件的值,怎么改?有很多方法啦,但是$listeners给我们提供了一个新的思路。话不多说,直接上代码

  1. <template>
  2. <div>
  3. <childcom :name="name" :age="age" :sex="sex" @testChangeName="changeName"></childcom>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. 'name':'test',
  9. props:[],
  10. data(){
  11. return {
  12. 'name':'张三',
  13. 'age':'30',
  14. 'sex':'男'
  15. }
  16. },
  17. components:{
  18. 'childcom':{
  19. props:['name'],
  20. template:`<div>
  21. <div>我是子组件 {{name}}</div>
  22. <grandcom **v-bind="$attrs" v-on="$listeners"**></grandcom>
  23. </div>`,
  24. components: {
  25. 'grandcom':{
  26. template:`<div>我是孙子组件-------<button @click="grandChangeName">改变名字</button></div>`,
  27. methods:{
  28. grandChangeName(){
  29. this.$emit('testChangeName','kkkkkk')
  30. }
  31. }
  32. }
  33. }
  34. }
  35. },
  36. methods:{
  37. changeName(val){
  38. this.name = val
  39. }
  40. }
  41. }
  42. </script>

页面渲染如下

$listeners可以让你在孙子组件触发变yeye组件的事件,是不是很方便

Vue-inheritAttrs-attrs-listeners-详解 - 图10