組件(Component)是 Vue.js 最強大的功能之一。html
組件能夠擴展 HTML 元素,封裝可重用的代碼。vue
組件系統讓咱們能夠用獨立可複用的小組件來構建大型應用,幾乎任意類型的應用的界面均可以抽象爲一個組件樹:web
props
和 $emit
使用props,父組件可使用props向子組件傳遞數據。vuex
<template> <son :message="son"></son> </template> <script> import son from './son.vue'; export default { components: { son }, data () { return { message: 'father message'; } } } </script>
<template> <div>{{message}}</div> </template> <script> export default { /** * 獲得父組件傳遞過來的數據 * 這裏的定義最好是寫成數據校驗的形式,省得獲得的數據是咱們意料以外的 * * props: { * message: { * type: String, * default: '' * } * } * */ props:['message'], } </script>
父組件向子組件傳遞事件方法,子組件經過$emit觸發事件,回調給父組件。數組
<template> <son @getChildData="getChildData"></son> </template> <script> import son from './son.vue'; export default { components: { son }, methods: { // 執行子組件觸發的事件 getChildData(val) { console.log(val); } } } </script>
<template> <div> <input type="text" v-model="myMessage" @input="passData(myMessage)"> </div> </template> <script> export default { props: { msg: { type: String, required: true } }, data() { return { // 這裏是必要的,由於你不能直接修改 props 的值 myMessage: this.message } }, methods () { passData(val) { // 數據狀態變化時觸發父組件中的事件 this.$emit('getChildData', val); } } } </script>
在上面的例子中,有父組件 father 和子組件 son。緩存
第一種方式處理父子組件之間的數據傳輸有一個問題:若是父組件A下面有子組件B,組件B下面有組件C,這時若是組件A想傳遞數據給組件C怎麼辦呢?
若是採用第一種方法,咱們必須讓組件A經過prop傳遞消息給組件B,組件B在經過prop傳遞消息給組件C;要是組件A和組件C之間有更多的組件,那採用這種方式就很複雜了。Vue 2.4開始提供了$attrs和$listeners來解決這個問題,可以讓組件A之間傳遞消息給組件C。dom
$attrs
:包含了父做用域中不被 prop 所識別 (且獲取) 的特性綁定 (class 和 style 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含全部父做用域的綁定 (class 和 style 除外),而且能夠經過 v-bind="$attrs" 傳入內部組件。一般配合 inheritAttrs 選項一塊兒使用。ide
$listeners
:包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它能夠經過 v-on="$listeners" 傳入內部組件ui
<template> <div> <p>我是父組件</p> <son :son="son" :grandSon="grandSon" @getSonData="getSonData" @getGrandSonData="getGrandSonData"></son> </div> </template> <script> import son from './son.vue'; export default { components: { son }, data() { return { son: '爺爺的兒子', grandSon: '爺爺的孫子' } }, methods: { // 來自子組件觸發的事件 getSonData(val) { console.log(val) }, // 來自孫組件觸發的事件 getGrandSonData(val) { console.log(val) } } } </script>
<template> <div> <p>我是子組件</p> <input type="text" v-model="myMessage" @input="passData(myMessage)"> <grandson :child="child" v-bind="$attrs" v-on="$listeners"></grandson> </div> </template> <script> import son from './son.vue'; export default { components: { grandson }, props: ['son'], data() { return { child: '爸爸的兒子', myMessage: '' } }, // 默認爲true,若是傳入的屬性子組件沒有prop接受,就會以字符串的形式出現爲標籤屬性 // 設爲false,在dom中就看不到這些屬性,試一下就知道了 inheritAttrs: false, created () { // 在子組件中打印的$attrs就是父組件傳入的值,刨去style,class,和子組件中已props的屬性 console.log(this.$attrs) // 打印爺爺的孫子 }, methods:{ passData(val) { // 觸發父組件中的事件 this.$emit('getSonData', val) } } } </script>
<template> <div> <p>我是孫組件</p> <input type="text" v-model="myMessage" @input="passData(myMessage)"> {{$attrs.grandSon}} {{$attrs.child}} </div> </template> <script> export default { data() { return { myMessage: '' } }, inheritAttrs: false, created () { // 打印爺爺的孫子和爸爸的兒子 console.log(this.$attrs) }, methods:{ passData(val) { // 觸發父組件中的事件 this.$emit('getGrandSonData', val) } } } </script>
在上面的例子中,咱們定義了 父,子,孫 三個組件,其中組件子是組件父的子組件,組件孫是組件子的子組件。this
父組件經過v-model傳遞值給子組件時,會自動傳遞一個value的prop屬性,在子組件中經過this.$emit(‘input', val)自動修改v-model綁定的值
<template> <div> <p>我是父組件</p> {{message}} <son v-model="message"></son> </div> </template> <script> import son from './son.vue'; export default { components: { son }, data() { return { message: '個人兒子' } } } </script>
<template> <div> <p>我是子組件</p> <input type="text" v-model="myMessage" @input="passData(myMessage)"> </div> </template> <script> export default { //v-model會自動傳遞一個字段爲value的prop屬性 props: ['value'], data() { return { myMessage: this.message } }, methods:{ passData(val) { //經過如此調用能夠改變父組件上v-model綁定的值 this.$emit('input', val) } } } </script>
在上面的實例代碼中,咱們定義了 father 和 son 兩個組件,這兩個組件是父子關係,v-model 也只能實現父子組件之間的通訊。
v-model
綁定了 myMessage 屬性和一個 input 事件。當 input 值變化時,就會觸發 input 事件,處理 father 組件經過 v-model
給 son 組件綁定的 input
事件,觸發 father 組件中 message
屬性值的變化,完成 child
子組件改變 father 組件的屬性值。父組件中經過provider來提供變量,而後在子組件中經過inject來注入變量。不論子組件有多深,只要調用了inject那麼就能夠注入provider中的數據。而不是侷限於只能從當前父組件的prop屬性來獲取數據,只要在父組件的生命週期內,子組件均可以調用。
<template> <div> <p>我是父組件</p> </div> </template> <script> export default { provide:{ message: '中午一塊兒吃飯' } } </script>
<template> <div> <p>我是子組件</p> {{message}} //會顯示中午一塊兒吃飯 </div> </template> <script> export default { inject:['message'],//獲得父組件傳遞過來的數據 } </script>
在上面的實例中,咱們定義了組件 father
和組件 son
,組件 father 和組件 son 是父子關係。
provide
屬性,以對象的形式向子孫組件暴露了一些屬性inject
屬性注入了 father 組件提供的數據,實際這些經過 inject
注入的屬性是掛載到 Vue 實例上的,因此在組件內部能夠經過 this 來訪問。$parent
和 $children
這裏要說的這種方式就比較直觀了,直接操做父子組件的實例。$parent
就是父組件的實例對象,而 $children
就是當前實例的直接子組件實例了,不過這個屬性值是數組類型的,且並不保證順序,也不是響應式的。
<template> <div> <p>我是父組件</p> <button @click="changeChildValue">test</button> <son></son> <grandson></grandson> </div> </template> <script> import son from './son.vue' import grandson from './grandSon.vue' export default { components: { son, grandSon }, data() { return { fatherMessage: '' } } methods: { changeChildValue(){ this.$children[0].sonMessage = 'hello'; } } } </script>
<template> <div> <p>我是子組件</p> <input type="text" v-model="mymessage" @change="changeValue" /> </div> </template> <script> export default { data() { return { mymessage: this.$parent.fatherMessage } }, methods: { changeValue(){ this.$parent.fatherMessage = this.mymessage;//經過如此調用能夠改變父組件的值 } } } </script>
this.$children[0].sonMessage = 'hello';
給
son
組件內的 sonM
essage
屬性賦值,而在 son 子組件中,一樣也是直接經過
this.$parent.fatherMessage
給
father
組件中的 fatherMessage 賦值,造成了父子組件通訊。
對於父子組件之間的通訊,上面的兩種方式是徹底能夠實現的,可是對於兩個組件不是父子關係,那麼又該如何實現通訊呢?在項目規模不大的狀況下,徹底可使用中央事件總線 EventBus
的方式。
在組件以外定義一個bus.js做爲組件間通訊的橋樑,適用於比較小型不須要vuex又須要兄弟組件通訊的
import Vue from 'vue' export const Bus = new Vue();
import Bus from './bus.js' export default { methods: { bus () { Bus.$emit('msg', '我要傳給兄弟組件們') } } }
import Bus from './bus.js' export default { mounted() { Bus.$on('msg', (e) => { console.log(e) }) } }
中央事件總線 EventBus
很是簡單,就是任意組件和組件之間打交道,沒有多餘的業務邏輯,只須要在狀態變化組件觸發一個事件,而後在處理邏輯組件監聽該事件就能夠。