Vue組件通信方式全面詳解


vue組件通信方式全面詳解

眾所周知,Vue主要思想就是組件化開發。因為,在實際的項目開發中,肯定會以組件的開發模式進行。形如頁面和頁面之間需要通信一樣,Vue 組件和組件之間肯定也需要互通有無、共享狀態。接下來,我們就悉數給大家展示所有 Vue 組件之間的通信方式。

組件關系

  • App組件和A組件、A組件和B組件、B組件和C組件形成父子關系
  • B組件和D組件形成兄弟關系
  • App組件和C組件、App和B組件形成了隔代關系(其中的層級可能是多級,既隔多代)

組件通信


這么多的組件關系,那么組件和組件之間又有哪些通信的方式呢?各種方式的區別又是什么?適用場景又是什么呢?

props和$emit

這種方式是我們日常開發中應用最多的一種方式。

props以單向數據流的形式可以很好的完成父子組件的通信

所謂單向數據流:就是數據只能通過 props 由父組件流向子組件,而子組件並不能通過修改 props 傳過來的數據修改父組件的相應狀態。至於為什么這樣做,Vue 官網做出了解釋:

**所有的 prop 都使得其父子 prop 之間形成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,但是反過來則不行。這樣會防止從子組件意外改變父級組件的狀態,從而導致你的應用的數據流向難以理解。

額外的,每次父級組件發生更新時,子組件中所有的 prop 都將會刷新為最新的值。這意味着你不應該在一個子組件內部改變 prop。如果你這樣做了,Vue 會在瀏覽器的控制台中發出警告。

正因為這個特性,於是就有了對應的 $emit$emit 用來觸發當前實例上的事件。對此,我們可以在父組件自定義一個處理接受變化狀態的邏輯,然后在子組件中如若相關的狀態改變時,就觸發父組件的邏輯處理事件。

let Child = {
   template: `<div>
<input type="text" v-model='msg'/>
<button @click='handleClick'>傳遞</button>
</div>`,
   props: ['msg'],
   methods: {
       handleClick() {
           this.$emit('getChildData', '子組件數據')
       }
   },

}
let Parent = {
   data() {
       return {
           msg: '小馬哥',
           val:''
       }
   },
   methods: {
       getChildData(val) {
           this.val = val;
       }
   },
   template: `
<div>
<p>我是一個父組件</p>
<p>我是{{val}}</p>
<Child :msg='msg' @getChildData='getChildData'></Child>
</div>
`,
   components: {
       Child
   }
}

let vm = new Vue({
   el: '#app',
   template: `
<div>
<Parent></Parent>
</div>
`,
   components: {
       Parent
   }

})
  1. 父傳子:父組件傳遞msg數據給子組件,通過v-bind綁定msg,子組件中直接可以用props接收綁定的數據
  2. 子傳父:子組件觸發相應的事件,通過$emit觸發事件傳遞數據,父組件中綁定對應的事件,通過$on監聽對應的事件 接收子組件傳遞的數據

EventBus-中央事件總線

如果想實現兄弟組件之間進行通信,在項目規模不大的情況下,完全可以使用中央事件總線EventBus的方式。如果你的項目規模是大中型的,那我們會使用vuex狀態管理

EventBus通過新建一個Vue事件bus對象,通過bus.$emit觸發事件,bus.$on監聽觸發的事件。

Vue.component('A', {
    template: `
<div>
<p>我是A組件</p>
<button @click='handleClick'>A傳遞到B</button>
</div>
`,
    data() {
        return {
            msg: 'hello 小馬哥'
        }
    },
    methods: {
        handleClick() {
            this.$bus.$emit('globalEvent',this.msg);
        }
    },
})
Vue.component('B', {
    template: `
<div>
<p>我是B組件</p>
<h3>{{aValue}}</h3>
</div>
`,
    data() {
        return {
            aValue: ''
        }
    },
    created () {
        this.$bus.$on('globalEvent',(val)=>{
            this.aValue = val;
        })
    },
})
// 定義中央事件總線
let bus = new Vue();
// 將中央事件總線賦值給Vue.prototype中,這樣所有組件都能訪問到了
Vue.prototype.$bus = bus;

let vm = new Vue({
    el: '#app',
    template: `
<div>
<A></A>
<B></B>
</div>
`,


})

$attrs和$listeners

通過 props進行組件通信的方式只適合直接的父子組件,如果父組件A下面有子組件B,組件B下面有組件C,這時如果組件A直接想傳遞數據給組件C那就行不通了! 只能是組件A通過 props 將數據傳給組件B,然后組件B獲取到組件A 傳遞過來的數據后再通過 props 將數據傳給組件C。當然這種方式是非常復雜的,無關組件中的邏輯業務一種增多了,代碼維護也沒變得困難,再加上如果嵌套的層級越多邏輯也復雜,無關代碼越多!

針對這樣一個問題,Vue 2.4提供了$attrs$listeners來實現能夠直接讓組件A傳遞消息給組件C

Vue.component('A', {
      template: `
      <div>
        <p>我是A組件</p>
        <B :msg='msg' @getCData='getCData'></B>
      </div>
     `,
      methods: {
        getCData(val) {
          alert(val)
        }
      },
      data() {
        return {
          msg: 'hello 小馬哥'
        }
      },
    })
    Vue.component('B', {
      template: `
      <div>
        <p>我是B組件</p>
         <!-- C組件中能直接觸發 getCData 的原因在於:B組件調用 C組件時,使用 v-on 綁定了 $listeners 屬性 -->
         <!-- 通過v-bind 綁定 $attrs 屬性,C組件可以直接獲取到 A組件中傳遞下來的 props(除了 B組件中 props聲明的) -->
        <C v-bind='$attrs' v-on='$listeners'></C>
      </div>
     `,
      // props: ['msg'],
      data() {
        return {

        }
      }
    })
    Vue.component('C', {
      template: `
    <div>
      <p>我是C組件</p>
      <p>{{$attrs.msg}}</p>
      <button @click='handleClick'>傳遞數據</button>
    </div>
    `,
      methods: {
        handleClick() {
          this.$emit('getCData', 'C組件的數據')
        }
      },
      data() {
        return {

        }
      }
    })
    let vm = new Vue({
      el: '#app',
      template: `
      <div>
        <A></A>
      </div>
      `,


    })
  • $attrs:包含了父作用域中不被 prop 所識別 (且獲取) 的特性綁定 (classstyle 除外)。當一個組件沒有聲明任何 prop 時,這里會包含所有父作用域的綁定屬性 (class和 style 除外),並且可以通過 v-bind="$attrs" 傳入內部組件。
  • $listeners:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過 v-on="$listeners" 傳入內部組件。

provide和inject

在父組件中通過 provider 來提供屬性,然后在子組件中通過 inject 來注入變量。不論子組件有多深,只要調用了 inject 那么就可以注入在 provider 中提供的數據,而不是局限於只能從當前父組件的 prop 屬性來獲取數據,只要在父組件的生命周期內,子組件都可以調用。這和 React 中的 Context API 有沒有很相似!

Vue.component('A', {
      template: `
      <div>
        <p>我是A組件</p>
        <B></B>
      </div>
     `,
      provide:{
        a:"祖先A的數據"
      },
      data() {
        return {
          msg: 'hello 小馬哥'
        }
      },
    })
    Vue.component('B', {
      template: `
      <div>
        <p>我是B組件</p>
        <C></C>
      </div>
     `,

      data() {
        return {

        }
      }
    })
    Vue.component('C', {
      template: `
    <div>
      <p>我是C組件</p>
      <h3>{{a}}</h3>
    </div>
    `,
    inject:['a'],
      data() {
        return {

        }
      }
    })
    let vm = new Vue({
      el: '#app',
      template: `
      <div>
        <A></A>
      </div>
      `,


    })
  • parent 組件中,通過 provide 屬性,以對象的形式向子孫組件暴露了一些屬性
  • child 組件中,通過 inject 屬性注入了 parent 組件提供的數據,實際這些通過 inject 注入的屬性是掛載到 Vue 實例上的,所以在組件內部可以通過 this 來訪問

⚠️ 注意:官網文檔提及 provide 和 inject 主要為高階插件/組件庫提供用例,並不推薦直接用於應用程序代碼中。

$parent和$children

這里要說的這種方式就比較直觀了,直接操作父子組件的實例。$parent 就是父組件的實例對象,而 $children 就是當前實例的直接子組件實例了,不過這個屬性值是數組類型的,且並不保證順序,也不是響應式的。

Vue.component('Parent', {
     template: `
     <div>
       <p>我是父組件</p>
       {{msg}}
       <hr/>
       <Child></Child>
     </div>
    `,
     mounted () {
       //讀取子組件數據,注意$children並不保證順序,也不是響應式的
       console.log(this.$children[0].a)
     },
     data() {
       return {
         msg: 'hello 小馬哥'
       }
     },
   })
   Vue.component('Child', {
     template: `
     <div>
       <p>我是孩子組件</p>
       <input type="text" @input='changeValue'/>
       <h2>{{myMsg}}</h2>
     </div>
    `,
     methods: {
       changeValue() {
         this.$parent.msg = '子組件中的數據'
       }
     },
     data() {
       return {
         myMsg:this.$parent.msg,
         a:"小馬哥"
       }
     }
   })
   
   let vm = new Vue({
     el: '#app',
     template: `
     <div>
       <Parent></Parent>
     </div>
     `,
   })

Vuex狀態管理

Vuex 是狀態管理工具,實現了項目狀態的集中式管理。工具的實現借鑒了 FluxRedux、和 The Elm Architecture 的模式和概念。當然與其他模式不同的是,Vuex 是專門為 Vue.js 設計的狀態管理庫,以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。詳細的關於 Vuex 的介紹,你既可以去查看官網文檔,也可以查看本專欄關於 Vuex 一系列的介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM