$attrs
属于vue的实例属性,说到实例属性想必用到最多的当属$refs
了,而其他的属性我们可能比较陌生。 谈到$attrs
我们要和$props
区别开来。
1.$props
和 $attrs
是什么
首先通过官方文档了解二者使用:
$props: 当前组件接收到的 props 对象
$attrs: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。
当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),
并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
文档说的还是蛮清晰的,下面通过具体的小例子感受一下
2.例子
子组件定义:
<template>
<div>这是车辆信息卡片</div>
</template>
<script>
export default {
props: {
test: String
},
data() {
return {}
},
created() {
console.log('props', this.$props)
console.log('attrs', this.$attrs)
}
}
</script>
如上代码中,子组件定义了props。
父组件定义:
<div>
<PreviewCard>
<StoreInfoCard :storeInfo="storeInfo" :test="'123'"/>
</PreviewCard>
</div>
如上代码中父组件额外传递了storeInfo属性,在子组件中通过$attrs获取, 如下图所示
如果子组件的定义如下:
<template>
<div>这是车辆信息卡片</div>
</template>
<script>
export default {
data() {
return {}
},
created() {
console.log('props', this.$props)
console.log('attrs', this.$attrs)
}
}
</script>
没有定义props,那么所有传递的数据都被识别为attrs
我们发现,只有子组件定义了props,$props
才能获取到。如果子组件没有有定义可以通过$attrs获取。
3.使用$attrs
进行组件封装
在对UI组件进行二次封装时,使用$attrs更加方便,并且这样可以减少子组件props中定义很多属性的工作量。我们通过对el-dialog的封装来体会一下。
3.1通过定义props的方式:
<template>
<el-dialog :title="title"
:visible.sync="innerVisible"
:close-on-click-modal="closeOnClickModal"
:center="center"
:append-to-body="appendToBody"
:class="dialogClass"
:lock-scroll="true"
@open="open"
@opened="opened"
@close="close"
@closed="closed"
:ref="refName"
:before-close="handleClose">
<el-scrollbar ref="dialogScrollbar">
<div class="dialog-content">
<slot/>
</div>
</el-scrollbar>
<template slot="footer">
<slot slot="footer"
name="footer"></slot>
</template>
<slot slot="footer"
name="footer"></slot>
</el-dialog>
</template>
<script>
export default {
name: 'MyDialog',
props: {
// Dialog 的标题
title: String,
// 是否显示 Dialog,支持 .sync=修饰符
visible: {
type: Boolean,
default: false
},
// 是否可以通过点击 modal 关闭 Dialog
closeOnClickModal: {
type: Boolean,
default: true
},
// 自身是否插入至 body 元素上
appendToBody: {
type: Boolean,
default: false
},
// 是否对头部和底部采用居中布局
center: {
type: Boolean,
default: true
},
refName: {
type: String,
default: 'mxDialog'
},
handleClose:Function
},
}
</script>
3.2 通过$attrs
定义的dialog
<template>
<el-dialog v-drag class="dialog" v-bind="attrs" v-on="$listeners">
<template v-for="item in slots" v-slot:[item]>
<slot :name="item">
<div v-if="item === 'title'" :key="item" class="dialog-title">
{{ attrs.title }}
</div>
<span v-if="item === 'footer' && showFooter" :key="item">
<el-button size="small" @click="handleCancel">取 消</el-button>
<el-button
size="small"
:loading="loading"
type="primary"
@click="handleConfirm"
>确 定</el-button
>
</span>
</slot>
</template>
</el-dialog>
</template>
<script>
export default {
name: 'v-dialog',
inheritAttrs: false,
props: {
showFooter: {
type: Boolean,
default: true
},
loading: {
type: Boolean,
default: false
}
},
}
</script>
通过对比组件模板部分我们可以发现使用$attrs的方式定义组件更加简洁,不用定义那么多props, 这样的方式封装组件更加高效。
在第二个版本中我们在模板(template)部分还可以看到$listeners,文档中也有对其的定义:
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。
它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用.
总结
本文首先介绍了$prop
s和$attrs
两个常用API的含义,然后通过小例子了解了它们的使用方法,最后通过对比使用$attrs
和props定义的组件体会了使用$attrs
的简洁高效。