Vue $attrs 和 $listeners
这两个属性都是 vue 2.4.0
新增的, 一般在创建高级别的组件时使用, 今天我们就来研究一下
# $attrs
类型:
{ [key: string]: string }
只读
详细:
包含了父作用域中不作为
prop
被识别 (且获取) 的特性绑定 (class
和style
除外). 当一个组件没有声明任何prop
时, 这里会包含所有父作用域的绑定 (class
和style
除外), 并且可以通过v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用.
简单来讲就是: 假如父元素给子元素传递了 3
个特性绑定: a
, b
, c
, 而子元素在 prop
中只声明了 a
, 那么 $attrs
中现在就有 2
个特性: b
和 c
.
这个有什么用呢? 官方也说了, 在创建高级别的组件时非常有用, 那我们就通过一个小例子来了解一下吧
# 例子
这里就以 ElementUI
的 el-tree
为例子吧
假如我想要封装一下 el-tree
, 那我是不是要先创建一个组件 BaseTree
, 为了保证使用体验(咱封装组件一般都喜欢保留原组件的 Attributes
, 这样别人使用的时候按照原来的习惯使用即可), el-tree
的所有 props
我是不是需要在这个组件中全都声明一遍, 再传给 el-tree
.
那么我的 props
需要声明一大堆特性, 但这些特性其实我这个组件不用的, 只是为了传给 el-tree
, 而且 el-tree
也需要再把这些特性添加进去
props: {
// 本组件需要的特性
level: {
type: Number,
default: 1
},
// el-tree 需要的特性
data: {
type: Array,
default () {
return []
}
},
nodeKey: {
type: String,
default: ''
},
highlightCurrent: {
type: Boolean,
default: false
},
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<el-tree
:data="data"
:node-key="nodeKey"
:highlight-current="highlightCurrent"
...
>
</el-tree>
2
3
4
5
6
7
麻不麻烦, 太麻烦了
这时候就需要 $attrs
出场了.
本组件的 props
只声明本组件需要的特性, 其余的一律不管,
props: {
// 本组件需要的特性
level: {
type: Number,
default: 1
}
}
2
3
4
5
6
7
<el-tree v-bind="$attrs"></el-tree>
这样是不是一下就简单多了
万一本组件和 el-tree
都需要同一个特性怎么办呢? 那也好办呀. 在 props
中声明特性, 同时传递给 el-tree
props: {
// 本组件需要的特性
level: {
type: Number,
default: 1
},
// 本组件 和 el-tree 都需要的特性
data: {
type: Array,
default () {
return []
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
<el-tree v-bind="$attrs" :data="data"></el-tree>
# 注意事项
传入组件的特性会覆盖 $attrs
的同名特性
如:
<el-tree :highlight-current="false" v-bind="$attrs"></el-tree>
这样的话, 无论传入本组件的 highlight-current
是什么值, el-tree
接收到的都是 false
.
# $listeners
类型:
{ [key: string]: Function | Array<Function> }
只读
详细:
包含了父作用域中的 (不含
.native
修饰器的)v-on
事件监听器. 它可以通过v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用.
理解了 $attrs
后, 理解 $listeners
就方便很多了. 简单来讲就是: 假如在父元素中给子元素绑定了两个方法(不含 .native
修饰器的), 那么 $listeners
就包含这两个方法
# 例子
以 el-tree
的 check-change
为例子来讲解, 这个 Event
为节点选中状态发生变化时的回调
只能是绑定到 el-tree
上才能生效
<el-tree v-bind="$attrs" @check-change="handleCheckChange"></el-tree>
现在的情况是使用我们封装的组件后, 只能绑定到 base-tree
上, 无法绑定到 el-tree
上:
<base-tree @check-change="handleCheckChange"></base-tree>
怎么办? 难道还需要在本组件的 el-tree
上将全部 Event
绑定上, 然后再 $emit
出去? 当然不是了, 用 $listeners
呀
<el-tree v-bind="$attrs" v-on="$listeners"></el-tree>
这样就将绑定到 base-tree
上的 check-change
方法绑定到 el-tree
上了, 根本不用我们做什么处理
那有人要问了, 本组件也需要 check-change
这个方法呢? 那你也绑定上不就行了吗
<el-tree
v-bind="$attrs"
v-on="$listeners"
@check-change="handleCheckChange"
>
</el-tree>
2
3
4
5
6
# 注意事项
我们在 $attrs
中说了:
传入组件的特性会覆盖
$attrs
的同名特性
那传入的方法也会覆盖 $listeners
的同名方法吗?
结果却不是, 不但不会覆盖, 而且会同时触发: 当触发 el-tree
的 check-change
时, 不仅触发调用本组件的父元素的 handleCheckChange
, 也会触发本组件的 handleCheckChange
.
我猜测可能是 $attrs
传入的是基本类型的数据, 而 $listeners
传入的是方法, 是对象, 引用地址不同, 所以都保留下来了. 但 $attrs
测试传入对象也还是会覆盖.
这个目前还不知道, 可能需要看源码才能明白了
那假如我想要覆盖效果怎么办呢? 也好办:
<el-tree
v-bind="$attrs"
v-on="{ ...$listeners, 'check-change': handleCheckChange }"
>
</el-tree>
2
3
4
5
methods: {
handleCheckChange (data) {
let handlerData
// do something
this.$emit('check-change', handlerData)
}
}
2
3
4
5
6
7
注: 关于 check-change
这个事件名, 不能使用驼峰名代替
具体参见: 自定义事件 — Vue.js (opens new window)
不同于组件和 prop, 事件名不存在任何自动化的大小写转换. 而是触发的事件名需要完全匹配监听这个事件所用的名称.
- 01
- 搭配 Jenkins 实现自动化打包微前端多个项目09-15
- 02
- 自动化打包微前端多个项目09-15
- 03
- el-upload 直传阿里 oss 并且显示自带进度条和视频回显封面图06-05