vue-------組件之間通信

vue api強化學習之-------組件之間通信

除去vuex之外的方式,即vue內部組件之間的通信方式

props 和 $emit

props 是單向數據流方式,數據只能通過 props 由父組件流向子組件,而子組件並不能通過修改 props 傳過來的數據修改父組件的相應狀態
使用v-on在父組件引入子組件後的模板上監聽子組件中的事件,$emit 可以觸發使用v-on在父組件中監聽的事件;

代碼示例

  • 父組件communication.vue
<template>
  <div class="communication">
      <div>我是父組件</div>

       <div class="child">
           <!-- 使用v-on監聽子組件中的事件 myevent -->
           <child1 :arr="arr" @myevent="childEmit" ></child1>
           <div><span>子組件傳遞給父組件的數據:</span><span>{{childmsg}}</span></div>
       </div>
  
   
 
  </div>
</template>
<script>
import Child1 from './child1.vue'
export default {
  name: 'Communication',
  data(){
      return {
          arr:[
              {national:'蜀國',emperor:'劉備'},
              {national:'吳國',emperor:'孫權'},
              {national:'魏國',emperor:'曹操'},

            ],
            childmsg:"",
      }

  },
  components:{
      Child1,
  },
   methods:{
       // 子組件調用父組件的中的此方法
       childEmit(pra){
           this.childmsg=pra;
       }, 
   }






}
</script>
  
  • 子組件 child1.vue
<template>
  <div class="child1">
      <div>我是child1</div>
      <div>
          <span>我是從父組件中接受到的數據</span>
          <p v-for="item in paraentArr" :key="item.national">
              <span>{{item.national}}:</span>
              <span>{{item.emperor}}</span>
          </p>
      </div>
      <button @click="toParent">向父組件傳遞數據</button>

  </div>
</template>
<script>
export default {
  name: 'Child1',
  // 接受父組件傳遞過來的數據,並規定接受數據的類型
  props:{
      arr:{
          type:Array,
          default:[]
      }

  },
 
  data(){
      // 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
      return {
          paraentArr:this.arr
        
      }
  },
  methods:{
      toParent(){
          // 在此處觸發父組件中監聽的事件myevent,並向父組件傳遞數據
          this.$emit("myevent","父組件接受到了嗎?")
      },
  }

}
</script>

$parent 和 children/children/ref

通過this.parentthis.parent可以直接獲得子組件的直系父組件 通過this.chiildren可以獲得當前組件的直系子組件,不過獲取到的是一個實例數組,操作不太方便,利用子組件標籤的ref屬性,我們可以在父組件中直接使用this.$refs.xx獲取到子組件的數據和方法,

代碼示例

  • 父組件communication.vue
<template>
  <div class="communication">
      <div>我是父組件</div>

       <div class="child">
           // 子組件綁定ref
           <child2 ref="child2"></child2>
           <button @click="toChild2">向子組件傳數據</button><br/>

           <span>子組件傳遞過來的數據:{{fromchild2}}</span>

        </div> 
   
 
  </div>
</template>
<script>
import Child2 from './child2.vue'
export default {
  name: 'Communication',
  data(){
      return {
            fromchild2:''
      }

  },
  components:{
      Child2
  },
   methods:{
    
       // 通過$refs獲取子組件的方法,通過此方法向子組件傳遞數據
       toChild2(){
           this.$refs.child2.updatePra("父組件傳遞過來的值");

           // 通過$children獲取子組件的方法,通過此方法向子組件傳遞數據
           // 此處應該遍歷獲取到的vue實例數組,找到想要的子組件實例,因爲$children獲取到的子組件實例沒法保證順序
           console.log(this.$children[1],".........this.$children....");
           this.$children[1].updatePra('父組件傳遞過來的值>>>>>>>>')
       },

       // 子組件獲取到父組件的實例後,通過自方法向父組件傳遞數據
       child2UpdateData(msg){
           this.fromchild2 = msg;
       }
   }

}
</script>
  
  • 子組件 child2.vue
<template>
  <div class="child2">
   <div>我是child2</div>
   <div>
       <span>我是從父組件傳遞過來的:</span>
       <span>{{fromParent}}</span><br/>

       <button @click="toparent">toparent</button>

   </div>
 
  </div>
</template>
<script>
export default {
  name: 'Child2',
 
  data(){
      // 根級響應式屬性,全部在此處聲明
      return {
          fromParent:"",  
      }
  },
  methods:{
      // 父組件通過此方法向子組件傳參
      updatePra(msg){
          this.fromParent = msg
      },
      // 通過$parent獲取父組件實例對象,並向父組件傳遞數據
      toparent(){
          this.$parent.child2UpdateData("child2傳遞過來的數據,父組件收到了嗎?")
      }
  }
}
</script>

$on 和 $emit

$on 監聽當前實例上的自定義事件
$emit 觸發當前實例上的自定義事件
emitemit和on只能作用在一一對應的同一個組件實例上,因此要使用這兩個方法進行組件之間的通信,則需要引入一個空的vue實例

代碼示例

此示例是從child1組件向child2組件傳遞消息,若想要從child2向child1組件傳遞消息,按下面流程將觸發器跟監聽器再返回來寫一套即可;

  • 事件處理中心(一個空的vue實例) eventHub.js
import Vue from 'vue';

// 創建一個vue實例,用於組件之間的信息傳遞
const eventHub = new Vue();

export  default eventHub;
  • 子組件child1.vue (child1與child2爲兄弟組件)
<template>
  <div class="child1">
      <div>我是child1</div>
      // 從child1組件向child2組件傳遞消息,
      <div> 
          <button @click="child1ToChild2">child1向child2發送信息</button>
      </div>
 
  </div>
</template>
<script>
import eventHub from './eventHub.js'
export default {
  name: 'Child1',
  methods:{
      child1ToChild2(){
          // 通過一個外部vue實例,此實例相當於一個事件處理中心,觸發一個自定義事件
          eventHub.$emit("tochild2event",'從child1傳遞過來的消息');
      }
  }

}
</script>
  
  • 子組件 child2.vue
<template>
  <div class="child2">
   <div>我是child2</div>
   <div>
       <div>
           顯示從child1中獲取到的信息:{{fromchild1}}
       </div>
   </div>
 
  </div>
</template>
<script>
import eventHub from './eventHub.js'
export default {
  name: 'Child2',
 
  data(){
      // 根級響應式屬性,全部在此處聲明
      return {
          fromchild1:'',
        
      }
  },
  // 在此處需要首先監聽事件
  mounted(){
      const that = this;
      // 組件掛載之後開始監聽tochild2event事件,並進行回調函數的處理
      eventHub.$on("tochild2event",function(msg){
          that.fromchild1 = msg;
      })
  },

}
</script>

$attrs 和 $listeners

vm.attrspropsclassstylevm.attrs,包含了父作用域中沒有被props接受的屬性,class和style除外; vm.listeners,包含了父作用域中的v-on事件監聽器,不包括.native修飾符修飾的
$attrs 和 listeners,vbind=listeners 來能夠直線隔代組件之間的消息互通,中間組件可通過v-bind='attrs’ | v-on=’$listeners’將信息直接傳遞給其後代組件
$listeners 和 attrsattrs 兩者表面層都是一個意思,attrs 是向下傳遞數據,$listeners 是向下傳遞方法,底層組件中可直接手動調用 $listeners 對象裏的方法;

代碼示例

  • 父組件communication.vue
<template>
  <div class="communication">
      <div>我是父組件</div>
          <div>
            <span>從孫子組件傳遞過來的信息:</span>
            <span>{{fromGrandsonMsg}}</span>
        </div>

       <div class="child">
           <!-- 使用v-on監聽子組件中的事件 myevent grandsonCompmsg和grandEvent都是給孫子組件的 -->
           <child1 :arr="arr" @myevent="childEmit" :grandsonCompmsg="grandmsg" @grandEvent="grandCallMethod"></child1>
       </div>
  </div>
</template>
<script>
import Child1 from './child1.vue'
export default {
  name: 'Communication',
  data(){
      return {
          arr:[
              {national:'蜀國',emperor:'劉備'},
              {national:'吳國',emperor:'孫權'},
              {national:'魏國',emperor:'曹操'},

            ],
        
            grandmsg:"孫子,收到我的信息了嗎?",
            fromGrandsonMsg:""

      }

  },
  components:{
      Child1,
  },
   methods:{
       grandCallMethod(msg){
           this.fromGrandsonMsg = msg;
       }
   }

}
</script>
  
  • 子組件 child1.vue
<template>
  <div class="child1">
      <div>我是child1</div>
      <div>
          <span>我是從父組件中接受到的數據</span>
          <p v-for="item in paraentArr" :key="item.national">
              <span>{{item.national}}:</span>
              <span>{{item.emperor}}</span>
          </p>
      </div>
    
      <div>
          <!-- 此處通過 v-bind='$attrs'和v-on='$listeners'向孫子組件傳遞數據和方法 -->
          <child1-child v-bind="$attrs" v-on="$listeners"></child1-child>
      </div>
 
   
 
  </div>
</template>
<script>
import Child1Child from './child1_child.vue'
export default {
  name: 'Child1',
  // 爲true時,從父組件中傳入的,此組件沒有通過props接受的參數,都會自動成爲子組件根標籤上的屬性;此時 $attrs中也存在
  // 若爲false,則沒有被props接受的參數,都回保存到 $attrs中去
  inheritAttrs: false,
  components:{
      Child1Child
  },

  // 接受父組件傳遞過來的數據,並規定接受數據的類型
  props:{
      arr:{
          type:Array,
          default:[]
      }

  },
 
  data(){
      // 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
      return {
          paraentArr:this.arr
        
      }
  },
 
}
</script>
  • 子組件 child1_child.vue
<template>
  <div class="child1_child">
      <div>我是child1的child</div>
      <div>
          <span>我是從爺爺組件中獲取到的信息:{{fromGrandfarherMsg}}</span>
      </div>
      <button @click="$listeners.grandEvent('爺爺,你收到我發的信息了吧')">我要給爺爺發信息</button>
     
  </div>
</template>
<script>
export default {
  name: 'Child1Child',
  // 爲true時,從父組件中傳入的,此組件沒有通過props接受的參數,都會自動成爲子組件根標籤上的屬性;此時 $attrs中也存在
  // 若爲false,則沒有被props接受的參數,都回保存到 $attrs中去
  inheritAttrs: false,

 // 接受爺爺組件傳遞過來的數據,並規定接受數據的類型
  props:{
      grandsonCompmsg:{
          type:String,
          default:""
      }
  },
 
  data(){
     // 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
      return {
          fromGrandfarherMsg:this.grandsonCompmsg
      }
  },


}
</script>

v-model 和 :xx.sync

v-model通常用在表單組件的雙向數據綁定,當然,組件之前的通信也可用v-model來實現,組件之間使用v-model綁定參數時,默認綁定在value屬性上,同時默認給綁定了input事件;如:相當於:
組件屬性可通過sync修飾符,從而使得組件之間可進行雙向的數據綁定,使用sync顯示符時,此處在綁定一個屬性foo時,默認綁定了一個update:foo的事件,如下所示:

代碼示例

  • 父組件 communication.vue
<template>
  <div class="communication">
      <div>我是父組件</div>
      

       <div class="child">
           <!-- v-model給子組件綁定vmodelmsg時,同時默認綁定了input事件給子組件,子組件中使用默認使用value接受屬性,也可以在子組件中使用model選項自定義接受屬性名及事件名 使用$emit觸發事件即可-->
           <!--  此處使用sync綁定tosync屬性的同時,默認綁定了update:tosync事件給子組件,子組件中props中接受tosync屬性即可,使用$emit('update:tosync',xx)觸發事件即可-->
           <child1 
            v-model="vmodelmsg" :tosync.sync ="someMsg" >

       </div>
  </div>
</template>
<script>
import Child1 from './child1.vue'
export default {
  name: 'Communication',
  data(){
      return {
        vmodelmsg:"從父組件傳遞過來的vmodel_msg信息",
        someMsg:'從 父組件 .sync方式傳遞過來的信息......'

      }

  },
  components:{
      Child1,
  },
  
}
</script>

  • 子組件child1.vue
<template>
  <div class="child1">
      <div>我是child1</div>
     
      <div>
          通過v-model方式,從父組件傳遞過來的數據:
          {{fromparent_vmodel}}
          <br/>
           通過sync方式,從父組件傳遞過來的數據:
          {{fromsync}}
          <br/>
          <!-- 子改變父好像不起作用?? -->
         <button @click="changfather">vmodel事件改變父組件的值</button>
          <button @click="changfathersync">sync改變父組件的值</button>
      </div>
 
   
 
  </div>
</template>
<script>
export default {
  name: 'Child1',
  // 爲true時,從父組件中傳入的,此組件沒有通過props接受的參數,都會自動成爲子組件根標籤上的屬性;此時 $attrs中也存在
  // 若爲false,則沒有被props接受的參數,都回保存到 $attrs中去
  inheritAttrs: false,
 
 // 次數可以自定義接受v-model綁定的屬性及時間名稱
  model:{
      prop:"vmodel_msg",
      event:"vmodelevent"

  },
  // 接受父組件傳遞過來的數據,並規定接受數據的類型
  props:{
      vmodel_msg:String,
      tosync:String
  },
 
  data(){
      // 將props中接受到的數據緩存到此處是必要的,因爲你不能直接修改 props 的值
      return {

          fromparent_vmodel:this.vmodel_msg,
          fromsync:this.tosync
        
      }
  },
  methods:{
      changfather(){
          this.$emit('vmodelevent','子改變了父中傳過來的屬性')
      },
      changfathersync(){
          this.$emit('update:tosync',"sync......update..666")
      }
  },
  mounted(){
      console.log(this,"....this...........");
  }

}
</script>

provide 和 inject

provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性
inject 選項應該是:一個字符串數組,或一個對象,對象的 key 是本地的綁定名,value 是注入時的key
在父組件中通過 provider 來提供屬性,然後在子組件中通過 inject 來注入變量。不論子組件有多深,只要調用了 inject 那麼就可以注入在 provider 中提供的數據,而不是侷限於只能從當前父組件的 prop 屬性來獲取數據,只要在父組件的生命週期內,子組件都可以調用。這和 React 中的 Context API 有沒有很相似!

代碼示例

  • 父組件communication.vue
<template>
  <div class="communication">
      <div>我是父組件</div>
      
       <div class="child">
           <child1 ></child1>
        </div>
  </div>
</template>
<script>
import Child1 from './child1.vue'
export default {
  name: 'Communication',
  data(){
      return {
  
      }

  },
  // 使用provide注入數據
  provide:{
      providePra:[1,2,3]
  },
  components:{
      Child1,
  },


}
</script>
  • 子組件child1.vue
<template>
  <div class="child1">
      <div>我是child1</div>

      <div>
          <h1>父組件中provide傳遞的:{{fromParentProvide}}</h1>
      </div>

  </div>
</template>
<script>
export default {
  name: 'Child1',
// 此處使用字符串數據或者對象的形式都可以接受到
//   inject:['providePra'],
  inject:{
      'fromParentProvide':'providePra'
  },
}
</script>

中央事件總線EventBus

中央事件總線其實就是onon和emit在應用上的全局使用,上面是隻是在局部組件中引入使用,在全局使用測,在跟組件創建之前,先定義中央事件總線:const EventBus = new Vue();然後將中央事件總線賦值到 Vue.prototype 上,即Vue.prototype.EventBus=EventBus;this.EventBus = EventBus;這樣所有組件都能通過this.EventBus訪問到了,然後使用this.EventBus.EventBus.on和this.EventBus.EventBus.emit監聽和觸發事件,從而進行各種類型之間的組件通信

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