我们在键盘上留下的余温, 也将随时代传递到更远的将来


:D 获取中...

这两个属性都是 vue 2.4.0 新增的, 一般在创建高级别的组件时使用, 今天我们就来研究一下

$attrs

  • 类型: { [key: string]: string }

  • 只读

  • 详细:

    包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( classstyle 除外). 当一个组件没有声明任何 prop 时, 这里会包含所有父作用域的绑定 ( classstyle 除外), 并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用.

简单来讲就是: 假如父元素给子元素传递了 3 个特性绑定: a , b , c , 而子元素在 prop 中只声明了 a , 那么 $attrs 中现在就有 2 个特性: bc .

这个有什么用呢? 官方也说了, 在创建高级别的组件时非常有用, 那我们就通过一个小例子来了解一下吧

例子

这里就以 ElementUIel-tree 为例子吧

假如我想要封装一下 el-tree , 那我是不是要先创建一个组件 BaseTree , 为了保证使用体验(咱封装组件一般都喜欢保留原组件的 Attributes , 这样别人使用的时候按照原来的习惯使用即可), el-tree 的所有 props 我是不是需要在这个组件中全都声明一遍, 再传给 el-tree .

那么我的 props 需要声明一大堆特性, 但这些特性其实我这个组件不用的, 只是为了传给 el-tree , 而且 el-tree 也需要再把这些特性添加进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
props: {
// 本组件需要的特性
level: {
type: Number,
default: 1
},
// el-tree 需要的特性
data: {
type: Array,
default () {
return []
}
},
nodeKey: {
type: String,
default: ''
},
highlightCurrent: {
type: Boolean,
default: false
},
...
}
1
2
3
4
5
6
7
<el-tree
:data="data"
:node-key="nodeKey"
:highlight-current="highlightCurrent"
...
>
</el-tree>

麻不麻烦, 太麻烦了

这时候就需要 $attrs 出场了.

本组件的 props 只声明本组件需要的特性, 其余的一律不管,

1
2
3
4
5
6
7
props: {
// 本组件需要的特性
level: {
type: Number,
default: 1
}
}
1
<el-tree v-bind="$attrs"></el-tree>

这样是不是一下就简单多了

万一本组件和 el-tree 都需要同一个特性怎么办呢? 那也好办呀. 在 props 中声明特性, 同时传递给 el-tree

1
2
3
4
5
6
7
8
9
10
11
12
13
14
props: {
// 本组件需要的特性
level: {
type: Number,
default: 1
},
// 本组件 和 el-tree 都需要的特性
data: {
type: Array,
default () {
return []
}
}
}
1
<el-tree v-bind="$attrs" :data="data"></el-tree>

注意事项

传入组件的特性会覆盖 $attrs 的同名特性

如:

1
<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-treecheck-change 为例子来讲解, 这个 Event 为节点选中状态发生变化时的回调

只能是绑定到 el-tree 上才能生效

1
<el-tree v-bind="$attrs" @check-change="handleCheckChange"></el-tree>

现在的情况是使用我们封装的组件后, 只能绑定到 base-tree 上, 无法绑定到 el-tree 上:

1
<base-tree @check-change="handleCheckChange"></base-tree>

怎么办? 难道还需要在本组件的 el-tree 上将全部 Event 绑定上, 然后再 $emit 出去? 当然不是了, 用 $listeners

1
<el-tree v-bind="$attrs" v-on="$listeners"></el-tree>

这样就将绑定到 base-tree 上的 check-change 方法绑定到 el-tree 上了, 根本不用我们做什么处理

那有人要问了, 本组件也需要 check-change 这个方法呢? 那你也绑定上不就行了吗

1
2
3
4
5
6
<el-tree
v-bind="$attrs"
v-on="$listeners"
@check-change="handleCheckChange"
>
</el-tree>

注意事项

我们在 $attrs 中说了:

传入组件的特性会覆盖 $attrs 的同名特性

那传入的方法也会覆盖 $listeners 的同名方法吗?

结果却不是, 不但不会覆盖, 而且会同时触发: 当触发 el-treecheck-change 时, 不仅触发调用本组件的父元素的 handleCheckChange , 也会触发本组件的 handleCheckChange .

我猜测可能是 $attrs 传入的是基本类型的数据, 而 $listeners 传入的是方法, 是对象, 引用地址不同, 所以都保留下来了. 但 $attrs 测试传入对象也还是会覆盖.

这个目前还不知道, 可能需要看源码才能明白了

那假如我想要覆盖效果怎么办呢? 也好办:

1
2
3
4
5
<el-tree
v-bind="$attrs"
v-on="{ ...$listeners, 'check-change': handleCheckChange }"
>
</el-tree>
1
2
3
4
5
6
7
methods: {
handleCheckChange (data) {
let handlerData
// do something
this.$emit('check-change', handlerData)
}
}

注: 关于 check-change 这个事件名, 不能使用驼峰名代替

具体参见: 自定义事件 — Vue.js

不同于组件和 prop, 事件名不存在任何自动化的大小写转换. 而是触发的事件名需要完全匹配监听这个事件所用的名称.


 评论

 无法加载Disqus评论系统,请确保您的网络能够正常访问。