Vue进阶之attrs
周末随手看Vue的api文档,看到了vm.$attrs
这个,官方是这么解释的:
包含了父作用域中不作为prop
被识别 (且获取) 的attribute
绑定 (class
和style
除外)。当一个组件没有声明任何prop
时,这里会包含所有父作用域的绑定 (class
和style
除外),并且可以通过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
属性我么是没有用的,那么下浏览器渲染出来是什么样呢?如下图:
- 组件内未被注册的属性将作为普通
html
元素属性被渲染! - 在组件里我们可以通过其
$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);
}
}
}
渲染效果如下:
优势
假如我们要做一个页面,有父组件,子组件,孙子组件,如下:
<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
的话就必须在父组件把name
和age
的值通过props
传递到子组件,子组件在通过props
把age
的值传递到孙子组件!
可是孙子组件需要的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>
子组件绑定了$attrs
,孙组件就能获取到除了name
属性外所有由父组件传递下来的属性。如果孙组件也想获取到name
属性那么,需要绑定个name
如下,
<grand v-bind="$attrs" :name="name"></grand>
关于inheritAttrs
默认情况下父作用域的不被认作props
的attribute
绑定 (attribute bindings
) 将会“回退”且作为普通的HTML attribute
应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置inheritAttrs
为false
,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例property $attrs
可以让这些attribute
生效,且可以通过v-bind
显性的绑定到非根元素上。这个选项不影响class
和style
绑定。
又是一段晦涩难懂的句子。
还是来一个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"
,子组件里input
上 type="number"
,那渲染到页面会是什么样?渲染图如下:
需求:我需要input
上type="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>
默认情况下vue
会把父作用域的不被认作 props
的特性绑定 且作为普通的 HTML
特性应用在子组件的根元素上。绑定就绑定,显示就显示,没啥大不了的,但是怕就怕遇到一些特殊的,就比如上面的input
这种情况,这个时候inheritAttrs:false
的作用就出来啦。