Vue--$attrs, $listeners--使用/教程/实例
原文网址:Vue--$attrs, $listeners--使用/教程/实例_IT利刃出鞘的博客-CSDN博客
简介
说明
本文用示例介绍Vue的$attrs和$listeners的用法。
使用场景
$attrs:用于父组件隔代向孙组件传值。
$listeners:用于孙组件隔代向父组件传值。
当然,这两个也可以同时使用,达到父组件和孙组件双向传值的目的。
官网
$attrs 和 $listeners介绍
Vue2.4 中,引入了attrs 和 listeners , 新增了 inheritAttrs 选项。
$attrs:包含了父作用域中没有被 prop 接收的所有属性(不包含class 和 style 属性)。可以通过 v-bind="$attrs" 直接将这些属性传入内部组件。
$listeners:包含所有父组件中的 v-on 事件监听器 (不包含 .native 修饰器的) ,可以通过 v-on="$listeners" 传入内部组件。
inheritAttrs为true:继承除props之外的所有属性;inheritAttrs为false:只继承class属性
$attrs
简介
现有3个嵌套组件,A->B,B->C。
A组件传值给C组件,有多少种解决方案?
Vuex我们使用vuex来进行数据管理,依赖于vuex我们可以一次改变,任何一个组件中都能获取。但是如果多个组件共享状态比较少,使用vuex过于麻烦和难以维护。element-ui中大量采用此方法。
自定义vue bus事件总线,原理类似vuex,使用特别简单。bus适合碰到组件跨级兄弟组件等无明显依赖关系的消息传递,原生app开发中经常用到,但是缺点是bus破坏了代码的链式调用,大量的滥用将导致逻辑的分散,出现问题后很难定位,降低了代码可读性。
使用B来做中转,A传递给B,B再给C**,**这是最容易想到的方案,但是如果嵌套的组件过多,需要传递的事件和属性较多,会导致代码繁琐,代码维护困难。
示例:父组件隔代传值给孙组件
代码
Parent.vue(父组件(顶层组件))
<template> <div class="outer"> <h3>父组件</h3> 名字:<input v-model="name"> 年龄:<input v-model.number="age" type="number"> 电话:<input v-model="phoneNumber"> <child :name="name" :age="age" :phoneNumber="phoneNumber"></child> </div> </template> <script> import Child from "./Child"; export default { name: 'Parent', components: {Child}, data() { return { name: 'Tony', age: 20, phoneNumber: '1234567890' } } } </script> <style scoped> .outer { margin: 20px; border: 2px solid red; padding: 20px; } </style>
Child.vue(子组件(中间组件))
子组件作为父组件和孙组件的传递中介,在儿子组件中给孙子组件添加v-bind="$attrs",这样孙子组件才能接收到数据。
<template> <div class="outer"> <h3>子组件</h3> <div>获得顶层组件的name:{{name}}</div> <grand-child v-bind="$attrs"></grand-child> </div> </template> <script> import GrandChild from "./GrandChild"; export default { components: {GrandChild}, props: ['name'], created() { console.log('Child=> $attrs: ' + JSON.stringify(this.$attrs)); } } </script> <style scoped> .outer { margin: 20px; border: 2px solid blue; padding: 20px; } </style>
GrandChild.vue(孙组件(最底层组件))
孙组件使用props接收从父组件传递过来的数据。(也可以在created时接收数据)
<template> <div class="outer"> <h3>孙组件</h3> <div>顶层组件的name:{{name}}</div> <div>顶层组件的age:{{age}}</div> <div>顶层组件的phoneNumber:{{phoneNumber}}</div> </div> </template> <script> export default { name: "GrandChild", props: { name: { type: String }, age: { type: Number }, phoneNumber: { type: String } }, created() { // this.parentAge = this.age; //也可以这样取值 console.log('GrandChild=> $attrs: ' + JSON.stringify(this.$attrs)); } } </script> <style scoped> .outer { margin: 20px; border: 2px solid green; padding: 20px; } </style>
路由(store/index.js)
import Vue from 'vue' import Router from 'vue-router' import Parent from "../components/Parent"; Vue.use(Router) export default new Router({ routes: [ { path: '/parent', name: 'Parent', component: Parent, } ], })
测试
测试1:访问
访问:http://localhost:8080/#/parent
一开始,父组件和孙组件的数据不一致,都是初始数据。
测试2:孙组件动态传值给父组件
实际应用
应用1:封装组件
element-ui开发的后台项目中,大量使用到了el-table和el-pagination做分页数据展示,所以我封装一个自定义组件page-table:
<template> <div class="page-table"> <div class="wrapper"> <el-table ref="elTable" :data="tableData"> <slot/> </el-table> <div style="margin-top: 16px;overflow: hidden"> <el-pagination class="page" :current-page="currentPage" layout="total, prev, pager, next, jumper" :total="total" @current-change="handleCurrentChange"/> </div> </div> </div> </template>
这样做的副作用是:引用page-table的地方无法使用el-table和属性和事件。
解决方案:在el-table使用的地方加上v-on="$listeners"和v-bind="$attrs",这样使用page-table的地方可使用所有el-table的属性和事件。
<template> <div class="page-table"> <div class="wrapper"> <el-table ref="elTable" v-bind="$attrs" v-on="$listeners" :data="tableData"> <slot/> </el-table> <div style="margin-top: 16px;overflow: hidden"> <el-pagination class="page" :current-page="currentPage" layout="total, prev, pager, next, jumper" :total="total" @current-change="handleCurrentChange"/> </div> </div> </div> </template>
应用2:兄弟组件传值到最外层
有时候会碰到多个兄弟组件传递参数到最外层,如有B组件包含C1和C2,都需要和A交互,定义2个props使用v-bind即可。
<template> <div class="page-table"> <div class="wrapper"> <el-table ref="elTable" v-bind="table1Props" :data="tableData"> <slot/> </el-table> <el-table ref="elTable" v-bind="table2Props" :data="tableData"> <slot/> </el-table> <div style="margin-top: 16px;overflow: hidden"> <el-pagination class="page" :current-page="currentPage" layout="total, prev, pager, next, jumper" :total="total" @current-change="handleCurrentChange"/> </div> </div> </div> </template> <script> export default { props: { table1Props: Object, table2Props: Object, } </script>