上一篇我们说到provide/inject的依赖注入的传值方法,今天我们来说一下另一个父组件给孙子组件的传值方式$attrs
我们接着上一个例子继续来写
parent父组件
我们给child子组件穿了四个属性值(title,name,hobby,age)
<template> <div> <button @click="add" >点击增加年龄</button> <child :title="title" :name='name' :hobby='hobby' :age='age'/> </div> </template> <script> import child from '@/components/child' export default { components:{child}, data () { return { title:'自我介绍', name:'Tom', hobby:'like eat', age:4, } }, methods:{ add(){ this.age ++ }, } } </script>
child子组件
child子组件使用props来接受,在child组件中引入childChild子组件,并使用v-bind将$attrs绑定在组件上,
就可在childChild组件中使用props或$attrs接收,按此规律可一直传递。
$attrs打印出来是一个对象,所以我们可以在子组件直接取你想要的数据值{{$attrs.name}}
当然当你的props不是空数组的话,也可以直接用{{name}}
<template> <section> <div>我是子组件:{{$attrs}}</div> <childChild v-bind="$attrs"/> </section> </template> <script> import childChild from '@/components/childChild' export default { props:[], // props:['name','age'], //props:['title','name','hobby','age'], components:{ childChild }, } </script>
看上面👆child组件中,我们注释的几个props,props他主要分三个情况,我们可以打印出来看一下
1. 当child组件的props为[]时,$attrs会打印出所有的对象值
child组件中:props:[],
2. 当child组件的props为['name','age']时,$attrs会打印出除props里面属性值得对象
child组件中:props:['name','age'],
3. 当child组件的props为['title','name','hobby','age']时,当把所有的父组件传来的值都写到props的时候,$attrs会打印出空对象
child组件中:props:['title','name','hobby','age'],
childChild组件
<template> <div> <div>我是子组件的组件:{{$attrs}}</div> <div>主题:{{$attrs.title}}</div> <div>姓名:{{$attrs.name}}</div> <div>爱好:{{$attrs.hobby}}</div> <div>年龄:{{$attrs.age}}</div> </div> </template>
当然点击parent父组件的add按钮的时候(增加年龄),孙子组件也是会响应的
那当我们想现在在孙子组件里改变父组件的值,怎么办?$listeners给我们提供了一个思路,
我们只需要现在子组件引入的组件上绑定一个
<childChild v-bind="$attrs" v-on="$listeners"/>
在孙子组件用就$emit方法,这个不多说,相信很多小伙伴都懂了
<template> <div> <div>我是子组件的组件:{{$attrs}}</div> <div>主题:{{$attrs.title}}</div> <div>姓名:{{$attrs.name}}</div> <div>爱好:{{$attrs.hobby}}</div> <div>年龄:{{$attrs.age}}</div> <button @click="childChildChangeName" >改名字</button> </div> </template> <script> export default { inject:['newFoo'], methods:{ childChildChangeName(){ this.$emit('changeName','Lili') } } } </script>
父组件就可以直接修改值了
<template> <div> <button @click="add" >点击增加年龄</button> <child :title="title" :name='name' :hobby='hobby' :age='age' @changeName='changeName'/> </div> </template> <script> import child from '@/components/child' export default { components:{child}, data () { return { title:'自我介绍', name:'Tom', hobby:'like eat', age:4, } }, methods:{ add(){ this.age ++ }, changeName(val){ this.name = val } } } </script>
$attrs可以传函数吗?
经过测试发现$attrs不仅可以传对应参数,也可以传函数,我们下面以之前的例子说明一下
还是之前的parent父组件, 我们给child组件上传了一个changeName函数,因为组件太多我们暂时就已一代二代来区分子组件
parent父组件
<template> <div> <button @click="add" >点击增加年龄</button> <child :title="title" :name='name' :hobby='hobby' :age='age' :changeName='changeName' @changeTitle='changeTitle'/> </div> </template> <script> import child from '@/components/child' export default { components:{child}, data () { return { title:'自我介绍', name:'Tom', hobby:'like eat', age:4, } }, methods:{ add(){ this.age ++ }, changeName(val){ this.name = val }, changeTitle(val){ this.title = val
this.name ='张三' } } } </script>
第一代组件
下面是我们的child第一代子组件,同时我们也相继的给第二代组件childChild上在传一个函数,后面继续试一下子组件相互修改值
<button @click="$attrs.changeName(data)">一代组件修改父组件值</button>
我们打印出$attrs看一下
<template> <section> <div>我是一代子组件:{{$attrs.title}}</div> <div>一代活动名称:{{activity.name}}</div> <div>一代活动地点:{{activity.place}}</div> <childChild v-bind="$attrs" v-on="$listeners" :changePlace='val => activity.place = val' /> <button @click="$attrs.changeName(data)">一代组件修改父组件值</button> </section> </template> <script> export default { data () { return { data:'小明', activity:{ name:'活动名称', date:'活动时间', place:'学校' }, } }, props:[], // props:['name','age'], // props:['title','name','hobby','age'], components:{childChild}, created(){ console.log(this.$attrs) }, } </script>
我们可以看到我们直接在第一点组件中调用$attrs.changeName(),并在里面传了一个定义的data值,修改name为data的‘小明’,点击按钮确实是可以触发,并修改name值,name值作为$attrs传的值,也会一起响应触发修改所有
我们可以看一下页面,name值被修改成功,同时,所有的$attrs值都被重新挂载修改
所以我们也可以不通过$emit来修改父组件的值,可以把函数作为三参数传给$attrs
第二代组件
我们上面给第二代子组件上也传了一个函数,我们这次试一下第三代组件修改第二代组件得值,我们看一下打印出来的数据
我们传的changePlace(),也打印出来了
我们在第二代组件里面又传了一个第三代组件,同时我们也给第三代组件穿了一个函数
childChildChangeName()是我们第三代组件将要执行的的反方法
<template> <div> <div>我是子组件的组件:{{$attrs}}</div> <div>主题:{{$attrs.title}}</div> <div>姓名:{{$attrs.name}}</div> <div>爱好:{{$attrs.hobby}}</div> <div>年龄:{{$attrs.age}}</div> <div>二代组件值:{{twoData}}</div> <threeChild v-bind="$attrs" :changeTwoName='childChildChangeName'></threeChild> </div> </template> <script> import threeChild from '@/components/threeChild' export default { components:{threeChild}, data () { return { twoData:'西游记' } }, methods:{ childChildChangeName(){ this.$emit('changeTitle','重新自我介绍') this.twoData = '水浒传' this.$attrs.title = '安居客'//发现没有修改 } } } </script>
第三代组件
这是我们第三代组件
我们可以回头看看第一代组件的页面
<childChild v-bind="$attrs" v-on="$listeners" :changePlace='val => activity.place = val' />
<template> <section> <div>我是第三代组件:{{$attrs}}</div> <button @click="changeOne">三修改一代组件</button> <button @click="changeTwo">三修改二代组件</button> </section> </template> <script> export default { methods:{ changeOne(){ this.$attrs.name='数据库打开'//发现不可以 this.$attrs.changePlace('西校区') }, changeTwo(){ this.$attrs.changeTwoName() } }, mounted(){ }, created(){ console.log('three',this.$attrs) }, } </script>
我们点击按钮 one ,发现直接修改$attrs的属性值是不奏效的,在调取他的changePlace(),并传入修改的值
我们看一下修改后的页面,点击按钮确实触发了,
我们接着点击two按钮试一下,他调取的是第二组件的方法,第二组建的方法同时又用$emit,去触发了父组件的一个方法,同时也试着修改了该组件的$attrs值
我们可以看上面第二代组件的代码回顾一下
<threeChild v-bind="$attrs" :changeTwoName='childChildChangeName'></threeChild>
具体方法是
childChildChangeName(){ this.$emit('changeTitle','重新自我介绍') this.twoData = '水浒传' this.$attrs.title = '安居客'//发现没有修改 }
修改的页面如下,我们发现除了直接修改$attrs的值不行,其他都可以,这说明要想修改$attrs的值,只能在传值的父组件中修改,其他的都不可以