8種vue組件通信方式詳細(xì)解析實(shí)例
對(duì)于vue來(lái)說(shuō),組件是非常常見的,有很多平臺(tái)都封裝了了屬于自己一套的組件,如element ui、we ui等等。同時(shí)組件之間的消息傳遞也是非常重要的,下面是我對(duì)組件之間消息傳遞的各種方式的總結(jié),共有8種方式。如有不足之處,可以留言補(bǔ)充,互相學(xué)習(xí)。
1. props和$emit
這是最最常用的父子組件通信方式,父組件向子組件傳遞數(shù)據(jù)是通過(guò)prop傳遞的,子組件傳遞數(shù)據(jù)給父組件是通過(guò)$emit觸發(fā)事件來(lái)做到的。 實(shí)例:
父組件
- Vue.component('parent',{
- template:`
- <div>
- <p>父組件</p>
- <child :message="message" v-on:getChildData="getChildData"></child>
- </div>
- `,
- data(){
- return {
- message:'Hello web秀'
- }
- },
- methods:{
- //執(zhí)行子組件觸發(fā)的事件
- getChildData(val){
- console.log(val)
- }
- }
- })
- var app=new Vue({
- el:'#app',
- template:`
- <div>
- <parent></parent>
- </div>
- `
- })
子組件
- Vue.component('child',{
- //得到父組件傳遞過(guò)來(lái)的數(shù)據(jù)
- props:['message'],
- data(){
- return {
- myMessage: this.message
- }
- },
- template:`
- <div>
- <input type="text" v-model="myMessage" @input="passData(myMessage)">
- </div>
- `,
- methods:{
- passData(val){
- //觸發(fā)父組件中的事件
- this.$emit('getChildData',val)
- }
- }
- })
解析:
- 父組件傳遞了message數(shù)據(jù)給子組件,并且通過(guò)v-on綁定了一個(gè)getChildData事件來(lái)監(jiān)聽子組件的觸發(fā)事件;
- 子組件通過(guò)props得到相關(guān)的message數(shù)據(jù),***通過(guò)this.$emit觸發(fā)了getChildData事件。
2. $attrs和$listeners
***種方式處理父子組件之間的數(shù)據(jù)傳輸有一個(gè)問(wèn)題:如果多層嵌套,父組件A下面有子組件B,組件B下面有組件C,這時(shí)如果組件A想傳遞數(shù)據(jù)給組件C怎么辦呢?
如果采用***種方法,我們必須讓組件A通過(guò)prop傳遞消息給組件B,組件B在通過(guò)prop傳遞消息給組件C;要是組件A和組件C之間有更多的組件,那采用這種方式就很復(fù)雜了。從Vue 2.4開始,提供了$attrs和$listeners來(lái)解決這個(gè)問(wèn)題,能夠讓組件A之間傳遞消息給組件C。
C組件
- Vue.component('C',{
- template:`
- <div>
- <input type="text" v-model="$attrs.messageC" @input="passCData($attrs.messageC)">
- </div>
- `,
- methods:{
- passCData(val){
- //觸發(fā)父組件A中的事件
- this.$emit('getCData',val)
- }
- }
- })
B組件
- Vue.component('B',{
- data(){
- return {
- myMessage:this.message
- }
- },
- template:`
- <div>
- <input type="text" v-model="myMessage" @input="passData(myMessage)">
- <C v-bind="$attrs" v-on="$listeners"></C>
- </div>
- `,
- //得到父組件傳遞過(guò)來(lái)的數(shù)據(jù)
- props:['message'],
- methods:{
- passData(val){
- //觸發(fā)父組件中的事件
- this.$emit('getChildData',val)
- }
- }
- })
A組件
- Vue.component('A',{
- template:`
- <div>
- <p>this is parent compoent!</p>
- <B
- :messageC="messageC"
- :message="message"
- v-on:getCData="getCData"
- v-on:getChildData="getChildData(message)">
- </B>
- </div>
- `,
- data(){
- return {
- message:'Hello',
- messageC:'Hello c'
- }
- },
- methods:{
- getChildData(val){
- console.log('這是來(lái)自B組件的數(shù)據(jù)')
- },
- //執(zhí)行C子組件觸發(fā)的事件
- getCData(val){
- console.log("這是來(lái)自C組件的數(shù)據(jù):"+val)
- }
- }
- })
- var app=new Vue({
- el:'#app',
- template:`
- <div>
- <A></A>
- </div>
- `
- })
解析:
- C組件中能直接觸發(fā)getCData的原因在于 B組件調(diào)用C組件時(shí) 使用 v-on 綁定了$listeners 屬性
- 通過(guò)v-bind 綁定$attrs屬性,C組件可以直接獲取到A組件中傳遞下來(lái)的props(除了B組件中props聲明的)
3. v-model
父組件通過(guò)v-model傳遞值給子組件時(shí),會(huì)自動(dòng)傳遞一個(gè)value的prop屬性,在子組件中通過(guò)this.$emit(‘input',val)自動(dòng)修改v-model綁定的值
子組件
- Vue.component('child',{
- props:{
- //v-model會(huì)自動(dòng)傳遞一個(gè)字段為value的prop屬性
- value: String,
- },
- data(){
- return {
- myMessage:this.value
- }
- },
- methods:{
- changeValue(){
- //通過(guò)如此調(diào)用可以改變父組件上v-model綁定的值
- this.$emit('input',this.myMessage);
- }
- },
- template:`
- <div>
- <input
- type="text"
- v-model="myMessage"
- @change="changeValue">
- </div>
- `
- })
父組件
- Vue.component('parent',{
- template:`
- <div>
- <p>this is parent compoent!</p>
- <p>{{message}}</p>
- <child v-model="message"></child>
- </div>
- `,
- data(){
- return {
- message:'Hello'
- }
- }
- })
- var app=new Vue({
- el:'#app',
- template:`
- <div>
- <parent></parent>
- </div>
- `
- })
4. provide和inject
父組件中通過(guò)provider來(lái)提供變量,然后在子組件中通過(guò)inject來(lái)注入變量。不論子組件有多深,只要調(diào)用了inject那么就可以注入provider中的數(shù)據(jù)。而不是局限于只能從當(dāng)前父組件的prop屬性來(lái)獲取數(shù)據(jù),只要在父組件的生命周期內(nèi),子組件都可以調(diào)用。
子組件
- Vue.component('child',{
- inject:['for'],//得到父組件傳遞過(guò)來(lái)的數(shù)據(jù)
- data(){
- return {
- myMessage: this.for
- }
- },
- template:`
- <div>
- <input type="tet" v-model="myMessage">
- </div>
- `
- })
父組件
- Vue.component('parent',{
- template:`
- <div>
- <p>this is parent compoent!</p>
- <child></child>
- </div>
- `,
- provide:{
- for:'test'
- },
- data(){
- return {
- message:'Hello'
- }
- }
- })
- var app=new Vue({
- el:'#app',
- template:`
- <div>
- <parent></parent>
- </div>
- `
- })
5. 中央事件總線
上面方式都是處理的父子組件之間的數(shù)據(jù)傳遞,那如果兩個(gè)組件不是父子關(guān)系呢?也就是兄弟組件如何通信?
這種情況下可以使用中央事件總線的方式。新建一個(gè)Vue事件bus對(duì)象,然后通過(guò)bus.$emit觸發(fā)事件,bus.$on監(jiān)聽觸發(fā)的事件。
- Vue.component('brother1',{
- data(){
- return {
- myMessage:'Hello brother1'
- }
- },
- template:`
- <div>
- <p>this is brother1 compoent!</p>
- <input type="text" v-model="myMessage" @input="passData(myMessage)">
- </div>
- `,
- methods:{
- passData(val){
- //觸發(fā)全局事件globalEvent
- bus.$emit('globalEvent',val)
- }
- }
- })
- Vue.component('brother2',{
- template:`
- <div>
- <p>this is brother2 compoent!</p>
- <p>brother1傳遞過(guò)來(lái)的數(shù)據(jù):{{brothermessage}}</p>
- </div>
- `,
- data(){
- return {
- myMessage:'Hello brother2',
- brothermessage:''
- }
- },
- mounted(){
- //綁定全局事件globalEvent
- bus.$on('globalEvent',(val)=>{
- this.brothermessage=val;
- })
- }
- })
- //中央事件總線
- var bus=new Vue();
- var app=new Vue({
- el:'#app',
- template:`
- <div>
- <brother1></brother1>
- <brother2></brother2>
- </div>
- `
- })
6. parent和children
- Vue.component('child',{
- props:{
- value:String, //v-model會(huì)自動(dòng)傳遞一個(gè)字段為value的prop屬性
- },
- data(){
- return {
- mymessage:this.value
- }
- },
- methods:{
- changeValue(){
- this.$parent.message = this.mymessage;//通過(guò)如此調(diào)用可以改變父組件的值
- }
- },
- template:`
- <div>
- <input type="text" v-model="mymessage" @change="changeValue">
- </div>
- })
- Vue.component('parent',{
- template:`
- <div>
- <p>this is parent compoent!</p>
- <button @click="changeChildValue">test</button >
- <child></child>
- </div>
- `,
- methods:{
- changeChildValue(){
- this.$children[0].mymessage = 'hello';
- }//在此我向大家推薦一個(gè)前端全棧開發(fā)交流圈:619586920 突破技術(shù)瓶頸,提升思維能力
- },
- data(){
- return {
- message:'hello'
- }
- }
- })
- var app=new Vue({
- el:'#app',
- template:`
- <div>
- <parent></parent>
- </div>
- `
- })
7. boradcast和dispatch
vue1.0中提供了這種方式,但vue2.0中沒(méi)有,但很多開源軟件都自己封裝了這種方式,比如min ui、element ui和iview等。 比如如下代碼,一般都作為一個(gè)mixins去使用, broadcast是向特定的父組件,觸發(fā)事件,dispatch是向特定的子組件觸發(fā)事件,本質(zhì)上這種方式還是on和on和emit的封裝,但在一些基礎(chǔ)組件中卻很實(shí)用。
- function broadcast(componentName, eventName, params) {
- this.$children.forEach(child => {
- var name = child.$options.componentName;
- if (name === componentName) {
- child.$emit.apply(child, [eventName].concat(params));
- } else {
- broadcast.apply(child, [componentName, eventName].concat(params));
- }
- });
- }
- export default {
- methods: {
- dispatch(componentName, eventName, params) {
- var parent = this.$parent;
- var name = parent.$options.componentName;
- while (parent && (!name || name !== componentName)) {
- parentparent = parent.$parent;
- if (parent) {
- name = parent.$options.componentName;
- }
- }
- if (parent) {
- parent.$emit.apply(parent, [eventName].concat(params));
- }
- },
- broadcast(componentName, eventName, params) {
- broadcast.call(this, componentName, eventName, params);
- }
- }
- };
8. vuex處理組件之間的數(shù)據(jù)交互
如果業(yè)務(wù)邏輯復(fù)雜,很多組件之間需要同時(shí)處理一些公共的數(shù)據(jù),這個(gè)時(shí)候才有上面這一些方法可能不利于項(xiàng)目的維護(hù),vuex的做法就是將這一些公共的數(shù)據(jù)抽離出來(lái),然后其他組件就可以對(duì)這個(gè)公共數(shù)據(jù)進(jìn)行讀寫操作,這樣達(dá)到了解耦的目的。