上一篇我們說到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的值,只能在傳值的父組件中修改,其他的都不可以

