vm.$attrs 【Vue 2.4.0新增inheritAttrs,attrs詳解】

描述:當我們組件嵌套層級多的時候,在Vue單向數據流向下傳遞需要不斷的props又不想用vuex的情況下來實現數據傳遞

1、首先我們來看下vue官方對vm.$attrs的介紹:
包含了父作用域中不作爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含所有父作用域的綁定 (class 和 style 除外),並且可以通過 v-bind="$attrs" 傳入內部組件——在創建更高層次的組件時非常有用。
猛一看有點看不明白....

2、場景介紹

vue中一個比較令人煩惱的事情是屬性只能從父組件傳遞給子組件。這也就意味着當你想向嵌套層級比較深組件數據傳遞,只能由父組件傳遞給子組件,子組件再傳遞給孫子組件...像下面這樣:

 

<parent-component :passdown="passdown">

<child-component :passdown="passdown">

<grand-child-component :passdown="passdown">

....

就這樣一層一層的往下傳遞passdown這個變量,最後才能用{{passdown}}。

假如我們需要傳遞的屬性只有1,2個還行,但是如果我們要傳遞的有幾個或者10來個的情況,這會是什麼樣的場景,我們會在每個組件不停的props,每個必須寫很多遍。有沒有其它方便的寫法?有,通過vuex的父子組件通信,的確這個是一個方法,但是還有其它的方法,這個就是我們要說的。通過inheritAttrs選項,以及實例屬性$attrs

3、實例:

 

<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屬性我麼是沒有用的,那麼下瀏覽器渲染出來是什麼樣呢?如下圖:

 

attrs.png

我們看到:組件內未被註冊的屬性將作爲普通html元素屬性被渲染,如果想讓屬性能夠向下傳遞,即使prop組件沒有被使用,你也需要在組件上註冊。這樣做會使組件預期功能變得模糊不清,同時也難以維護組件的DRY。在Vue2.4.0,可以在組件定義中添加inheritAttrs:false,組件將不會把未被註冊的props呈現爲普通的HTML屬性。但是在組件裏我們可以通過其$attrs可以獲取到沒有使用的註冊屬性,如果需要,我們在這也可以往下繼續傳遞。

如果我們在子組件裏設置 inheritAttrs: false:

 

components:{
    'mytest':{
      template:`<div>這是個h1標題{{title}}</div>`,
      props:['title'],
      inheritAttrs: false,
      data(){
        return{
          mag:'111'
        }
      },
      created:function(){
        console.log(this.$attrs)//注意這裏
      }
    }

渲染效果如下:

 

不繼承的情況.png

補充:說一下$attrs的使用

有一個頁面由父組件,子組件,孫子組件構成,如下:

 

<template>
    <div style="padding:50px;">
        <childcom :name="name" :age="age" :sex="sex"></childcom>
    </div>
</template>
<script>
export default {
    'name':'test',
    props:[],
    data(){
        return {
            'name':'張三',
            'age':'30',
            'sex':'男'
        }
    },
    components:{
        'childcom':{
            template:`<div>
                <div>{{name}}</div>
                <grandcom v-bind="$attrs"></grandcom>
            </div>`,
            props:['name'],
            components: {
                'grandcom':{
                    template:`<div>{{$attrs}}</div>`,
                }
            }
        }
    }
}
</script>

上面的代碼在頁面的效果是如下圖

 

attrs.png

如果attrs被綁定在子組件childcom上後,我們就可以在孫子組件grandcom裏獲取到this.$attrs的值。這個{{$attrs}}的值是父組件中傳遞下來的props(除了子組件childcom組件中props聲明的)。

記住孫子組件grandcom裏獲取到this.$attrs的值是除了子組件childcom聲明的元素!記住是除了子組件childcom聲明的元素!例如上面的代碼我在子組件childcom組件的props裏聲明瞭name,那麼我在孫子組件grandcom裏獲取到的$attrs就不包含name屬性,那麼this.$attrs = { 'age':'30', 'sex':'男'}。

說一下$attrs的優勢到底在哪

假如我們要做一個頁面,有父組件,子組件,孫子組件,如下:

 

<template>
    <div>
        <childcom></childcom>
    </div>
</template>
<script>
export default {
    'name':'test',
    props:[],
    data(){
        return {
            'name':'張三',
            'age':'30',
            'sex':'男'
        }
    },
    components:{
        'childcom':{
            template:`<div>
                <div>我是子組件</div>
                <grandcom></grandcom>
            </div>`,
            components: {
                'grandcom':{
                    template:`<div>我是孫子組件</div>`,
                }
            }
        }
    }
}
</script>

如上代碼,假如我想在子組件想獲取到父組件的name屬性值,在孫子組件獲取父組件的age屬性值,用props的話就必須在父組件把name和age的值通過props傳遞到子組件,子組件在通過props把age的值傳遞到孫子組件,到這裏看明白了吧,孫子組件需要的age在子組件裏沒有用到,但是爲了能讓孫子組件獲取到,你必須從父組件 傳到子組件,在在子組件傳遞到孫子組件。

但是用$attrs就不用那麼麻煩,如下:

 

<template>
    <div>
        <childcom :name="name" :age="age" :sex="sex"></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"></grandcom>
            </div>`,
            components: {
                'grandcom':{
                    template:`<div>我是孫子組件{{$attrs.age}}</div>`,
                }
            }
        }
    }
}
</script>

子組件綁定了"$attrs",孫子組件就能獲取到除了name屬性外所有由父組件傳遞下來的屬性。如果孫子組件也想獲取到name屬性那麼,在綁定個name如下,

 

 <grandcom v-bind="$attrs" :name="name"></grandcom>

細細體會下是不是這個道理。實在不行的話敲一敲代碼自己試驗下,你就會豁然開朗。

補充一下:inheritAttrs屬性

關於inheritAttrs這個屬性跟獲取到$attrs的值沒有關係,inheritAttrs通常在編寫基礎組件時候會用到。官網原話:默認情況下父作用域的不被認作 props 的特性綁定 (attribute bindings) 將會“回退”且作爲普通的 HTML 特性應用在子組件的根元素上。當撰寫包裹一個目標元素或另一個組件的組件時,這可能不會總是符合預期行爲。通過設置 inheritAttrs 到 false,這些默認行爲將會被去掉。而通過 (同樣是 2.4 新增的) 實例屬性 $attrs 可以讓這些特性生效,且可以通過 v-bind 顯性的綁定到非根元素上。

注意:這個選項不影響 class 和 style 綁定。

在Vue2.4.0之前版本,組件內未被註冊的屬性將作爲普通html元素屬性被渲染。

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':{
            props:['name','age'],
            template:`<input type="number" style="border:1px solid blue">`,
        }
    }
}
</script>

上面代碼你覺得input上會怎麼顯示? 父組件傳遞了type="text",子組件裏input 上type="number",那渲染到頁面會是什麼樣?渲染圖如下:

 

默認情況.png

看到沒,父組件傳遞的type="text"覆蓋了input 上type="number",這豈不是把我的input數據類型都給改變了,這豈不是有問題,這不是我想要的!!!!看到這裏明白了嗎?回頭去體會下上面官網的原話!!!

需求:我需要input 上type="number"類型不變,但是我還是要取到父組件的type="text"的值,那麼代碼如下:

 

<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>

頁面渲染圖如下:

 

需求.png

 

到這,我想大家都明白了inheritAttrs的作用了吧。默認情況下vue會把父作用域的不被認作 props 的特性綁定 且作爲普通的 HTML 特性應用在子組件的根元素上。綁定就綁定,顯示就顯示,沒啥大不了的,但是怕就怕遇到一些特殊的,就比如上面的input的情況,這個時候inheritAttrs:false的作用就出來啦。

順道補充一下:$listeners

父組件-子組件-孫子組件,,,,現在我要你在孫子組件裏改變父組件的值,怎麼改?有很多方法啦,但是$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>

頁面渲染如下:

 

$listers.png

$listeners可以讓你在孫子組件改變父組件的值,是不是很方便...........
鏈接:https://www.jianshu.com/p/ce8ca875c337

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章