Vue进阶之attrs

周末随手看Vue的api文档,看到了vm.$attrs这个,官方是这么解释的:

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

看完之后发现,这些解释比较晦涩,官方也没有具体的例子来解释!还是自己动手整个demo来看看吧!!!

场景:父组件传递给子组件属性

假如我们要传递一个title,这也就意味着当你想向嵌套层级比较深组件数据传递,只能由父组件传递给子组件,子组件再传递给孙子组件...像下面这样:

<parent-component :title="title">

<child-component :title="title">

<grand-child-component :title="title">

....

就这样一层一层的往下传递title这个变量,最后才能用{{title}}。

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

实例

<template>
  <div id="app">
    <my-test :title="title" :massgae="massgae"></my-test>
  </div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      title: '哈哈哈',
      massgae: '消息消息'
    };
  },
  components: {
    MyTest: {
      template: `<div>这是个h1标题:{{title}}</div>`,
      props: ['title'],
      data() {
        return {
          mag: '111'
        };
      },
      created: function() {
        console.log(this.$attrs);
      }
    }
  }
};
</script>

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

2020-07-17T11:40:16.png

  1. 组件内未被注册的属性将作为普通html元素属性被渲染!
  2. 在组件里我们可以通过其$attrs可以获取到没有使用的注册属性!

如果不想把未被注册的props呈现为普通的HTML属性,需要在组件中添加inheritAttrs:false

components: {
  MyTest: {
    template: `<div>这是个h1标题:{{title}}</div>`,
    props: ['title'],
    inheritAttrs: false,
    data() {
      return {
        mag: '111'
      };
    },
    created: function() {
      console.log(this.$attrs);
    }
  }
}

渲染效果如下:

2020-07-17T11:43:39.png

优势

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

<template>
  <div id="app">
    <child></child>
  </div>
</template>

<script>
export default {
  name: 'app',
  props: [],
  data() {
    return {
      name: '张三',
      age: '30',
      sex: '男'
    };
  },
  components: {
    child: {
      template: `
      <div>
        <div>我是子组件</div>
        <grand></grand>
      </div>`,
      components: {
        grand: {
          template: `<div>我是孙子组件</div>`
        }
      }
    }
  }
};
</script>

如上代码,假如我想在子组件想获取到父组件的name属性值,在孙子组件获取父组件的age属性值,用props的话就必须在父组件把nameage的值通过props传递到子组件,子组件在通过propsage的值传递到孙子组件!

可是孙子组件需要的age在子组件里没有用到,但是为了能让孙子组件获取到,你必须从父组件传到子组件,在在子组件传递到孙子组件。

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

<template>
  <div id="app">
    <child :name="name" :age="age" :sex="sex"></child>
  </div>
</template>

<script>
export default {
  name: 'app',
  props: [],
  data() {
    return {
      name: '张三',
      age: '30',
      sex: '男'
    };
  },
  components: {
    child: {
      props:['name'],
      inheritAttrs: false,
      template: `
      <div>
        <div>我是子组件:{{name}}</div>
        <grand v-bind="$attrs"></grand>
      </div>`,
      components: {
        grand: {
          inheritAttrs: false,
          template: `<div>我是孙组件:{{$attrs.age}}</div>`
        }
      }
    }
  }
};
</script>

2020-07-17T12:01:13.png

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

<grand v-bind="$attrs" :name="name"></grand>

关于inheritAttrs

默认情况下父作用域的不被认作 propsattribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrsfalse,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。这个选项不影响 classstyle 绑定。

又是一段晦涩难懂的句子。

还是来一个demo来说明问题吧!

<template>
  <div id="app">
    <child :name="name" :age="age" type="text"></child>
  </div>
</template>

<script>
export default {
  name: 'app',
  props: [],
  data() {
    return {
      name: '张三',
      age: '30',
      sex: '男'
    };
  },
  components: {
    child: {
      props: ['name', 'age'],
      template: `<input type="number" style="border:1px solid red">`
    }
  }
};
</script>

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

2020-07-17T12:08:26.png

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

<template>
  <div id="app">
    <child :name="name" :age="age" type="text"></child>
  </div>
</template>

<script>
export default {
  name: 'app',
  props: [],
  data() {
    return {
      name: '张三',
      age: '30',
      sex: '男'
    };
  },
  components: {
    child: {
      inheritAttrs: false,
      props: ['name', 'age'],
      template: `<input type="number" style="border:1px solid red">`,
      created() {
        console.log(this.$attrs.type);
      }
    }
  }
};
</script>

2020-07-17T12:10:25.png

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

添加新评论