说明:vue3小版本更新、补丁等带来的改动会导致可能文章讲解的内容与实际使用不一致。(已经碰上了。。),版本功能已稳定下来了,也不用怕。
版本:vue:3.2.31 ;
版本时间:2022-3-17 的最新版;
文章更新时间:2022-4-4
-
$attrs
-
类型:Object
-
仅可读
-
先讲解 $attrs ,这样与`inheritAttrs`结合起来更容易理解。
- 重点:包含了父作用域中不作为组件 `props` 或 `emit`自定义事件的 attribute 绑定和事件。
官方这句话后半段其实不太对,$attrs包含了所有 绑定在子组件上的v-on事件!跟JS原生一样的事件名则会继承到对应绑定的元素上。 - 当一个组件没有声明任何 props 时,这里会包含所有父作用域的绑定,并且可以通过 v-bind="$attrs" 传入内部组件——这在创建高阶的组件时会非常有用。
- 讲解涉及 v-bind 知识,若不了解,请先去看 v-bind ;
- ` $attrs `不包含内容补充:vue内置的2个特殊attribute,vue的所有内置指令、包括自定义指令;
- (但 v-on、v-bind 这2个包含,例如v-on:click会被解析成 onClick,而v-bind只是表示可动态绑定这个属性的值 。)
- 既然讲到了自定义指令,说一嘴:自定义指令绑定子组件时,若子组件是单根节点组件,则指令根节点会作用于根节点!与 inheritAttrs 继承和 $attrs 都无关!!
$attrs 对象不包含的有:
1、vue内置的特殊attribute:key ref。
2、vue的所有内置指令(v-on、v-bind 除外)
3、所有自定义的 directive 指令
4、当前组件的 props 内声明的所有 prop 名称
5、当前组件的 emits 内声明的所有自定义的事件名
$attrs 对象包含的内容有:
上面写的都不包含,那没写的当然都包含啦。。不然上面的我辛辛苦苦记录 列出来干啥。。
案例1:
// 父组件里 使用 nested 子组件 <nested style="color: rgb(0, 217, 255);" class='a' id="b" data-set='data测试' @click.once='attrsFn' @input='attrsFn' @blur='attrsFn' @customEmitsFn='customFn' :dynAttrs='{dy:666}' :fnAttr='attrsFn' :is='isdA' :testAttr='"这个是props,上面的全是测试$attrs包含的内容"' > </nested>
// 代码里 nested 子组件打印如下内容 <template> <div>无关内容</div> <div :='$attrs'>$attrs测试后</div> </template> <script> export default defineComponent({ inheritAttrs:false, props:['testAttr'], mounted(){ console.log(this.$attrs,'nested组件的attr'); console.log(this.$props,'nested组件的props'); } } </script> // 子组件在浏览器上的 html 结果 <div class="a" id="b" data-set="data测试" dynattrs="[object Object]" fnattr="function () { [native code] }" is="[object Object]" style="color: rgb(0, 217, 255);" >$attrs测试后</div>
该图为子组件的 $attrs 打印内容
案例2:
这种绑定方式会将$attrs对象上的所有键值对都绑定为该标签的上。
`.prop`、`.attr` 这2个特例,prop是元素的property,attr则是元素的attribute。(使用这2个修饰符的,在$attrs对象上的key值带有特殊前缀)
inheritAttrs 是true还是false,都与 $attrs 的使用无任何关系和影响。
但是 v-bind="$attrs" 是否有使用会影响 inheritAttrs 的警告是否提示!(下面 `inheritAttrs案例2`中有说明)
// 这是子组件 <template> <div calss='demosss'> <div class='child' ref="childRef" v-bind='$attrs'> <span v-bind='$attrs'>属性绑定测试</span> </div> </div> <div>....</div> <!-- 可再传给下个子组件 --> <nested v-bind='$attrs'></nested> </template> <script setup> import { useAttrs } from 'vue' const attrs = useAttrs() console.log(attrs); // {} :包含所有attr的键值对 </script>
原理:在 v-bind 指令中有如下写法,所以上述的 v-bind='$attrs' 等于将 非prop 的属性 绑定到指定的标签。
<!-- 绑定一个全是 attribute 的对象 --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
其他相关案例:
.prop
- 将一个绑定强制设置为一个 DOM property,参数是引用的形式。3.2+.attr
- 将一个绑定强制设置为一个 DOM attribute,参数固定是字符串。3.2+- 以上2个都能通过组件实例属性 $attrs 获取。
<template> <div :testShowWhat.prop="110">强制为prop,所以不会被当成 attribute </div> 1.<nested :testAttr.attr="'强制为attribute'" :testAttr='"这个就是props"' >tip:报错但有效,报错原因 vloar版本?</nested> 2.<!-- .testAttr 是 :testAttr.prop 的缩写; --> <!-- ^testAttr 是 :testAttr.attr 的缩写,这个文档实际没写; --> <nested :testAttr.prop="110"></nested> 3.<nested v-bind='$attrs'></nested> </template>
注意, `.prop`设置的并非是组件通信的props,是元素的 `property` ,所以给组件设置为`.attr`与`.prop`,组件设置了对应的props的属性名都无法接收到;
`.attr` 和 `.prop` 和 ` :testAttr="" `能一起使用。毕竟3个作用都不一样,不会互相影响,一个设置为标签attribute,一个是设置为标签元素的property,一个是作为组件通信的props。property获取方式:this.$refs.refElName.propertyName
-
inheritAttrs
-
类型:Boolean
-
默认:true
-
不会被继承的有:
vue内置的特殊attribute是不会继承的。
emits 选项中列出的事件不会被组件的根元素继承;
props 内声明的 prop 属性名也不会被当做 普通 attr 被继承。
请注意:指令不属于继承的范畴!
(v-on、v-bind 除外,v-on:click会被解析成 onClick,而v-bind只是表示可动态绑定这个属性的值 );
当v-xxx指令绑定到子组件时,该子组件如果是单根节点,则会添加到该根节点中,片段组件则指令直接无效,与 inheritAttrs 是true还是false都无关!!
会被继承的有:
1、当组件返回单个根节点时,非 prop 的 attribute 将自动添加到根节点的 attribute 中。
2、当组件返回单个根节点时,非 emit 声明的事件 将自动添加到根节点的 `原生DOM事件` 中。
案例1:inheritAttrs: true
// 父组件引用 <customEvent @click="testEvent" testAttr='testAttr_value' testAttr2='testAttr2' class="bushiba?" @myEvent="emitFN" ref="childtest" > </customEvent> // 子组件 customEvent <template> <div> 根节点这个 div 标签将继承非props的所有 attr 和 事件; (vue内置的特殊attribute是不会继承的) <div>单节点组件1</div> <div>单节点组件2</div> </div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ inheritAttrs: true, }) </script>
当template有根元素的时候,绑定到组件上的属性和事件会自动继承到根元素上
案例2:inheritAttrs: true ; 并且组件为多节点!
如果组件是片段(多节点),并且使用该组件时,组件标签绑定了attr和事件,这些都不会继承,并且会抛出警告!
也就是说,片段组件需要将 inheritAttrs 设置为 false。当然不设置为 false 也有个好处,抛出警告告诉编程人员,这个组件是多节点的!你对该组件标签设置的 attr 和 事件等全都无效!
// 父组件引用 <customEvent @click="abc" testAttr='testAttr_value' testAttr2='testAttr2' class="bushiba?" @myEvent="emitFN" ref="childtest" > </customEvent> // 子组件 customEvent <template> <div>无根节点组件:片段</div> <div>无根节点组件:片段</div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ inheritAttrs: true, }) </script>
其中一个重点回顾来了!
还记得上面的 $attr 吗,组件为多根节点,且 inheritAttrs: true 的时候,
如果该组件使用有` v-bind="$attrs" `,组件标签绑定了attr和事件,是不会抛出警告的!但是也不会继承这些 attr 和 事件。
案例3:inheritAttrs: false
懒得写案例了,直接说就都懂了。
设置为false后,不论单节点还是多节点的组件,都不会继承任何 attr 和 事件!
注意 `案例2` 所说的:【如果组件是片段(多节点),并且组件标签绑定了attr和事件,都不会继承,并且会抛出警告!】,但是在`inheritAttrs: false`的情况下,不会抛出警告!禁止继承的内容有:attribute、各种事件(emit事件除外)
记忆小结:
- inheritAttrs是继承,所以只需要关心被继承的组件(子组件)是否是单个根节点还是多个根节点(片段)就行了。
- ` v-bind="$attrs" ` 是将 `$attrs`对象里的内容绑定到该标签(元素),跟继承没任何关系!
虽然使用v-bind="$attrs" 后,inheritAttrs错误使用时不会抛出警告。但以上2个不要混为一谈,功能作用点不一样,也互不影响对方。
最后的最后:
这些内容,有很多细节,纠正过文章内容,现在应该没错的了,但万一嘛,又或者我理解还不够深,所以欢迎大家留言补充、交流、探讨!!!
QQ交流群:522976012 ,欢迎来玩。
聚焦vue3,但不限于vue,任何前端问题,究其本质,值得讨论,研究与学习。