前言
Vue 2.4.0 增加了 $attrs 和 $listeners 属性,先看一下该API的详细定义:
$attrs:
包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。
当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),
并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
$listeners:
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。
它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
与创建更高层次的组件相关:provide/inject
Vue 2.2.0 新增了 provide 和 inject 属性,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在上下游关系成立的时间里始终生效。主要为高阶插件/组件库提供用例。
实践
在父子组件中传参我们一般会通过props和emit,如果在高层级组件中,比如A->B->C,A与B互为父子,B与C互为父子,如果数据和方法都储存在A组件,那么C组件想要获得A组件的数据和方法的途径就是:A传给B,B再传给C。
今天就介绍多一种方法:$attrs 和 $listeners,A把B跟C需要的数据和方法都使用特性绑定,B拿到它需要的数据,然后将不需要的通过 v-bind="$attrs" 和 v-on="$listeners" 传递给C组件。
- 组件C
//指令
let BaseFocus = {
inserted(el, binding) {
el.focus = binding.value
}
};
let BaseInput = {
props: {
type: {
type: String,
default: 'text'
},
name: {
type: String,
default: 'text'
},
focus: {
type: Boolean,
default: false
},
styles: {
type: Object,
default: function() {
return {}
}
}
},
directives: {
focus: BaseFocus
},
template: `<input
:type="type"
:name="name"
@input="input"
@change="change"
v-focus="focus"
:style="styles" />`,
methods: {
input(e) {
this.$emit("input", e.target.value)
},
change(e) {
this.$emit("change", e.target.value)
}
}
};
- 组件B
let BaseCard = {
props: {
user_info: {
type: Object,
default: function() {
return {}
}
}
},
components: {
"base-input": BaseInput
},
template:
`<div class="base-card">
<div class="name">姓名:{{user_info.user_name}}</div>
<div class="gender">性别:{{user_info.user_gender}}</div>
<div class="test" @click="test">测试:Click me!!</div>
<div class="remark">
密码:<base-input v-bind="$attrs" v-on="$listeners"></base-input>
</div>
</div>`,
methods: {
test() {
this.$emit("test")
}
}
}
- 组件A
<div class="container" id="container">
<base-card
:user_info="user_info"
v-bind="user_input"
@test="test"
@input="input"
@change="change"></base-card>
</div>
- Vue实例
let vm = new Vue({
el: "#container",
data() {
return {
user_info: {
user_name: "carol",
user_gender: "female"
},
user_input: {
type: 'password',
name: 'passowrd',
focus: true
}
}
},
components: {
"base-card": BaseCard
},
methods: {
test() {
console.log("you just click the test div!!!")
},
input(val) {
console.log("your input: ", val)
},
change(val) {
console.log("you change the input and blur: ", val)
}
}
})
在上面的代码中,组件B中包含组件C时,我们的代码是这样写的:
<div class="remark">
密码:<base-input v-bind="$attrs" v-on="$listeners"></base-input>
</div>
借助这两个新增的API,我们在创建高层级,多层级嵌套的组件时会节约很多时间,代码看起来也会更整洁。
实践最终结果如图所示: