【小白读vue官方文档】vue中非prop的attribute、$attrs和禁用 Attribute 继承
注:props是properties的缩写,attrs是attributes的缩写,这两个词在一个叫“属性”,一个叫“特性”,很容易混淆,简单说来:
- property是DOM中的属性,是JavaScript里的对象;
- attribute是HTML标签上的特性,它的值只能够是字符串;
- 具体可以参考下这篇博文
具体到vue中,我的理解是,如果用v-bind
绑定(也就意味着子组件一定有props接收它,否则会报错),那它就是property
,本质上是JS变量传值;如果是用普通的等号传给子组件一个写死的值 (比如color="#21C9A6"
) 且子组件的props里有接收这个值(比如props里有 color: String
),那么它也是property
。
其他情况下,子组件没有props
来接收这个属性,那么就是非props的 attribute ,会被当作字符串内联到html文档里,也就是接下来要讲的东西。
非prop的attribute
来自vue官方文档的描述:
一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 prop 定义的 attribute。
举个例子:
自定义一个test组件,并接受父组件传来的test1 test2 class style 属性,其中 test1 属性在props里被接收,并在模板中显示用$attrs
显示。
<div id="app">
<test :test1='test1' test2='test2' class="container" style="color:red"></test>
</div>
<script>
Vue.component('test',{
props:['test1'],
template:`
<div>
{{this.$attrs}}
</div>
`
})
var vm = new Vue({
el:'#app',
data:{
test1:'haha'
}
})
</script>
关于$attrs
,vue的官方文档这么描述:
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (
class
和style
除外)。
最终$attrs
显示的结果如下,因为只有test属性满足:即不是class
,style
这种每个html标签共有的属性,也没有被子组件的props
接收,所以$attrs
中只有一个属性test2
并且我们可以看到,非props属性被当成字符串内联到了html文档中,就像class
和style
那样,虽然没有任何人知道test2
属性是什么意思,但不妨碍它存在。
禁用 Attribute 继承
默认情况下,子组件的根元素会接收父组件绑定来的attribute,如果想禁止这一行为,就需要在子组件里把inheritAttrs
属性手动设为false
(默认是true
)。这一点搭配子组件的$attrs
属性就可以起到指定子组件template中的哪一个元素来继承父组件绑定的attribute的作用。
例如,我这里不想让label元素继承test2,想让里面的div元素继承test2:
<div id="app">
<test :test1='test1' test2='test2' class="container" style="color:red"></test>
</div>
<script>
Vue.component('test',{
inheritAttrs: false, //禁止label根元素继承attribute
props:['test1'],
template:`
<label>
<div v-bind='$attrs'>
{{this.$attrs}}
</div>
</label>
`
})
var vm = new Vue({
el:'#app',
data:{
test1:'haha'
}
})
</script>
可以看到,test2被内联在了div而不是根元素label上。
接下来,理解官方文档中的例子就不难了:
<base-input>
组件中根元素是模板中的<label>
,然而<label>
绑定username数据,required
、placeholder
特性是没有卵用的,我们应该将它绑定给内层的<input>
标签,于是有了下面这段代码:
Vue.component('base-input', {
inheritAttrs: false, 禁止label根元素继承attribute
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
然后在html中就可以像用原生<input>
标签一样用<base-input>
了,而不用担心哪个元素是真正的根元素:
<base-input
v-model="username"
required
placeholder="Enter your username"
></base-input>