Henry Henry
  • JavaScript
  • TypeScript
  • Vue
  • ElementUI
  • React
  • HTML
  • CSS
  • 技术文档
  • GitHub 技巧
  • Nodejs
  • Chrome
  • VSCode
  • Other
  • Mac
  • Windows
  • Linux
  • Vim
  • VSCode
  • Chrome
  • iTerm
  • Mac
  • Obsidian
  • lazygit
  • Vim 技巧
  • 分类
  • 标签
  • 归档
  • 网站
  • 资源
  • Vue 资源
GitHub (opens new window)

Henry

小学生中的前端大佬
  • JavaScript
  • TypeScript
  • Vue
  • ElementUI
  • React
  • HTML
  • CSS
  • 技术文档
  • GitHub 技巧
  • Nodejs
  • Chrome
  • VSCode
  • Other
  • Mac
  • Windows
  • Linux
  • Vim
  • VSCode
  • Chrome
  • iTerm
  • Mac
  • Obsidian
  • lazygit
  • Vim 技巧
  • 分类
  • 标签
  • 归档
  • 网站
  • 资源
  • Vue 资源
GitHub (opens new window)
  • JavaScript

  • TypeScript

  • Vue

    • Vue 编码指南
    • vue-cli 启动本地服务局域网不能访问的原因分析
    • 解决 Vue 相同路由参数不同不会刷新的问题
    • 解决 vuex requires a Promise polyfill in this browser 问题
    • 关于父组件通过 v-on 接收子组件多个参数的一点研究
    • Vue $attrs 和 $listeners
      • \$attrs
        • 例子
        • 注意事项
      • \$listeners
        • 例子
        • 注意事项
    • Vue axios 发送 Form Data 数据格式请求
    • Vue 开发技巧
    • Vue 动态路由
    • Vue 集成 UEditor 富文本编辑器
    • Vue 修饰符
    • Vue 问题集合
    • Vue props 传多值的问题
    • vue-router 在 IE11 下手动更改 URL 的 hash 不会触发路由
    • vue-router 路由参数刷新消失的问题
    • 那些年被我们忽略的 vue 语法
    • vue 生命周期深入
    • vue 组件通信深入
    • vue 组件通信深入 Vuex
    • vue项目移动端、pc端适配方案
    • vuepress 如何引入 vuex
  • ElementUI

  • React

  • AntD

  • 前端
  • Vue
Henry
2020-04-06
目录

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
  },
  ...
}
1
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>
1
2
3
4
5
6
7

麻不麻烦, 太麻烦了

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

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

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

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

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

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

# 注意事项

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

如:

<el-tree :highlight-current="false" v-bind="$attrs"></el-tree>
1

这样的话, 无论传入本组件的 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>
1

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

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

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

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

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

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

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

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

具体参见: 自定义事件 — Vue.js (opens new window)

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

编辑 (opens new window)
#Vue
上次更新: 5/27/2023, 1:02:05 PM
关于父组件通过 v-on 接收子组件多个参数的一点研究
Vue axios 发送 Form Data 数据格式请求

← 关于父组件通过 v-on 接收子组件多个参数的一点研究 Vue axios 发送 Form Data 数据格式请求→

最近更新
01
version 1.15
07-01
02
version 1.14
06-27
03
version 1.13
06-27
更多文章>
Theme by Vdoing | Copyright © 2017-2023 HenryTSZ | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式