attrs
背景
随着项目复杂度的提高,组件嵌套的层级越来越深,之前的组件通信一般使用v-bind和props组合使用,但是我们发现这种方式只是适用于父子组件,如果是孙子组件的话就需要将父组件的数据传递给子组件,子组件的数据再传递给孙组件,这样子就需要写很多props,有没有哪种方式可以直接将父组件直接传递给孙组件,让代码更加简洁?这就是$attrs的由来,解决跨组件数据传递,注意只对孙子组件有效,而且class和style数据传递除外。
使用
首先我们有三个嵌套组件父A-子B-孙C,然后我们想让A中的数据传入C中,用props的做法是这样子的:
<div id="app">
A{{msg}}
<component-b :msg="msg"></component-b>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
msg: '100'
},
components: {
'ComponentB': {
props: ['msg'],
template: `<div>B<component-c :msg="msg"></component-c></div>`,
components: {
'ComponentC': {
props: ['msg'],
template: '<div>C{{msg}}</div>'
}
}
},
}
})
</script>
组件B并没有使用父组件传递过来的msg,而是直接传递给组件C,除了这样子有没有什么方式直接将数据传递给C呢,下面我们来看$attrs的写法:
<script>
let vm = new Vue({
el: '#app',
data: {
msg: '100'
},
components: {
'ComponentB': {
template: `<div>B<component-c v-bind="$attrs"></component-c></div>`,
components: {
'ComponentC': {
props: ['msg'],
template: '<div>C{{msg}}</div>'
}
}
},
}
})
</script>
总结:为了解决跨组件通信,避免多写props,而提出了$attrs,props和$attrs都可以用来父子组件通信,接收父组件传递过来的数据,但是props的优先级高于$attrs,如果子组件中props、$attrs都有写,那么数据只会被props接收,注意$attrs不能接收class和style传过来的数据。
inheritAttrs
背景
<template>
<div class="home">
<mytest :title="title" :massgae="massgae"></mytest>
</div>
</template>
<script>
export default {
name: 'home',
data () {
return {
title:'title1111',
massgae:'message111'
}
},
components:{
'mytest':{
template:`<div>这是个h1标题{{title}}</div>`,
props:['title'],
data(){
return{
mag:'111'
}
},
created:function(){
console.log(this.$attrs)//注意这里
}
}
}
}
</script>
上边的代码,我们在组件里只是用了title这个属性,massgae属性我么是没有用的,那么下浏览器渲染出来是什么样呢?如下图:
image.png
我们看到:组件内未被注册的属性将作为普通html元素属性在子组件的根元素上渲染,虽然在一般情况下不会对子组件造成影响,但是就怕遇到一些特殊情况,比如:
<template>
<childcom :name="name" :age="age" type="text"></childcom>
</template>
<script>
export default {
'name':'test',
props:[],
data(){
return {
'name':'张三',
'age':'30',
'sex':'男'
}
},
components:{
'childcom':{
props:['name','age'],
template:`<input type="number" style="border:1px solid blue">`,
}
}
}
</script>
image.png
我们看到父组件的type="text"覆盖了input上type="number",这不是我想要的,我需要input上type=number类型不变,但是我还是想取到父组件上的type="text"的值,这时候inheritAttrs就派上用场了。
<template>
<childcom :name="name" :age="age" type="text"></childcom>
</template>
<script>
export default {
'name':'test',
props:[],
data(){
return {
'name':'张三',
'age':'30',
'sex':'男'
}
},
components:{
'childcom':{
inheritAttrs:false,
props:['name','age'],
template:`<input type="number" style="border:1px solid blue">`,
created () {
console.log(this.$attrs.type)
}
}
}
}
</script>
image.png
总结:默认情况下父组件传递数据给子组件但是没被props的特性绑定将会回退且作为普通的html特性应用在子组件的根元素上。inheritAttrs属性用来去掉这种默认行为,来避免不可预知的影响。注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定。
$listeners
背景
上面讲了emit方法,但是子组件如果用不到,只是想改变父组件的数据,这时候我们就可以使用$listeners。
<template>
<div>
<childcom :name="name" :age="age" :sex="sex" @testChangeName="changeName"></childcom>
</div>
</template>
<script>
export default {
'name':'test',
props:[],
data(){
return {
'name':'张三',
'age':'30',
'sex':'男'
}
},
components:{
'childcom':{
props:['name'],
template:`<div>
<div>我是子组件 {{name}}</div>
<grandcom v-bind="$attrs" v-on="$listeners"></grandcom>
</div>`,
components: {
'grandcom':{
template:`<div>我是孙子组件-------<button @click="grandChangeName">改变名字</button></div>`,
methods:{
grandChangeName(){
this.$emit('testChangeName','kkkkkk')
}
}
}
}
}
},
methods:{
changeName(val){
this.name = val
}
}
}
</script>
listeners可以让你在孙子组件改变父组件的值,也是属于跨组件通信。`**
网友评论