關於Vue組件通信那點事

抽空梳理了一下Vue經常使用的組件之間的通信方式,而後想經過使用的技術進行分類整理筆記。vue

Prop傳值

使用範圍

父組件向子組件傳值。web

使用方法

父組件

<template>
  <div id="app">
      <div class="app">
          <div class="title">App.vue</div>
      </div>
        <!-- 將text1綁定給Component1 -->
      <component1 :text="text1" />
  </div>
</template>

export default {
    name: 'App',
    components: {
        Component1,
    },
    data(){
        return{
            text1:"Text From App.vue",
        }
    }
}
</script>
複製代碼

子組件

<template>
    <div class="component1">
        <div class="title">Component1</div>
        <div>
            <!-- 將父組件傳遞過來的text進行展現 -->
            <span class="text-bold">text:</span>
            {{text}}
        </div>
    </div>
</template>

<script>
export default {
    name: "Component1",
    // 經過props來接收text
    props:{
        text:{
            type:String
        }
    },
}
</script>
複製代碼

效果展現

截屏2020-10-13 上午9.55.04.png

注意

父組件經過Prop傳值給子組件是屬於單向數據流,所以當父組件修改該值的時候,子組件也會隨之更新數據;而子組件是不該該在內部改變 prop的。若是你能夠這麼作,可是Vue不推薦此作法,並會在控制檯發出警告。數組

v-on事件綁定

使用範圍

子組件調用父組件方法。markdown

使用方法

父組件

<template>
  <div id="app">
      <div class="app">
          <div class="title">App.vue</div>
          <div>
              <span class="text-bold">count:</span>
              {{count}}
          </div>
      </div>
        <!-- 將事件addCount綁定給Component1 -->
      <component1 @addCount="addCount" />
  </div>
</template>

<script>
import Component1 from "@/components/Component1";

export default {
    name: 'App',
    components: {
        Component1
    },
    data(){
        return{
            count:0
        }
    },
    methods:{
        addCount(){
            this.count++;
        }
    }
}
</script>
複製代碼

子組件

<template>
    <div class="component1">
        <div class="title">Component1</div>
        <!-- Component1的button觸發addParentCount -->
        <button @click="addParentCount">
            add count of App.vue
        </button>
    </div>
</template>

<script>
export default {
    name: "Component1",
    methods:{
        // 調用組件綁定的addCount方法
        addParentCount(){
            this.$emit('addCount')
        }
    }
}
</script>
複製代碼

效果展現

Kapture 2020-10-13 at 16.51.21.gif

注意

this.$emit() 的第一個參數爲事件綁定的EventName,而從第二個參數開始爲函數的參數,能夠傳多個參數值,意味着能夠經過此方法從子組件傳值給父組件。app

ref組件註冊

適用範圍

父組件獲取子組件的值或調用子組件的方法。ide

使用方法

父組件

<template>
  <div id="app">
      <div class="app">
          <div class="title">App.vue</div>
            <!-- 顯示獲取到的子組件的text -->
          <div>
              <span class="text-bold">text:</span>
              {{text2}}
          </div>
            <!-- 增長子組件的count值 -->
          <button @click="ChildrenCount">
              add count of Component1.vue
          </button>
      </div>
      <!-- 進行組件註冊 -->
      <component1 ref="component1" />
  </div>
</template>

<script>
import Component1 from "@/components/Component1";

export default {
    name: 'App',
    components: {
        Component1,
    },
    data(){
        return{
            text2:''
        }
    },
    mounted() {
        // 獲取子組件的text1值
        this.text2 = this.$refs.component1.text1 || '';
    },
    methods:{
        // 調用子組件的方法
        ChildrenCount(){
            this.$refs.component1.addCount();
        }
    }
}
</script>
複製代碼

子組件

<template>
    <div class="component1">
        <div class="title">Component1</div>
        <div>
            <span class="text-bold">count:</span>
            {{count}}
        </div>
    </div>
</template>

<script>
export default {
    name: "Component1",
    data(){
        return{
            // 父組件獲取到此值
            text1: 'Text From Component1.vue',
            count: 0
        }
    },
    methods:{
        // 父組件調用此方法
        addCount(){
            this.count++;
        }
    }
}
</script>
複製代碼

效果

Kapture 2020-10-13 at 17.20.54.gif

注意

this.$refs 是組件渲染後纔會進行填充,所以沒法在計算屬性 computed 使用,打印出來會顯示 undefinedsvg

EventBus事件總線

使用範圍

任意組件間的事件調用。函數

使用方法

初始化EventBus

共有兩種方式能夠初始化EventBus,第一種方式是新建一個 eventBus.js ,內容以下:ui

import Vue from 'vue'
export const EventBus = new Vue()
複製代碼

而後使用時均在兩個組件內引入該文件。this

import { EventBus } from "../eventBus.js";
複製代碼

而第二種方式是全局註冊EventBus。

在項目的 main.js 文件下,插入註冊代碼:

Vue.prototype.$EventBus = new Vue()
複製代碼

而後使用時便可經過 this.$EventBus 調用。

後面的案例均用第二種方法實現。

第一個組件

<template>
    <div class="component2">
        <div class="title">Component2</div>
            <!-- 獲取input值更新另外一個組件的count值 -->
        <div>
            <input type="number" placeholder="Number..." v-model="value">
        </div>
        <!-- 點擊button觸發事件 -->
        <button @click="changeComponent1Count">
            change count of Component1
        </button>
    </div>
</template>

<script>
export default {
    name: "Component2",
    data() {
        return {
            value:''
        }
    },
    methods:{
        // 在EventBus總線註冊changeCount事件,並將value值傳過去
        changeComponent1Count(){
            this.$EventBus.$emit("changeCount",this.value)
        }
    }
}
</script>
複製代碼

第二個組件

<template>
    <div class="component1">
        <div class="title">Component1</div>
        <div>
            <span class="text-bold">count:</span>
            {{count}}
        </div>
    </div>
</template>

<script>
export default {
    name: "Component1",
    data(){
        return{
            count: 0
        }
    },
    // 在mounted中監聽EventBus接收changeCount事件,並觸發回調函數將value值賦給count
    mounted(){
        this.$EventBus.$on('changeCount',(value)=> this.count = value);
    }
}
</script>
複製代碼

效果

Kapture 2020-10-13 at 17.41.28.gif

注意

雖然在Vue中可使用 EventBus 來做爲溝通橋樑的概念,就像是全部組件共用相同的事件中心,能夠向該中心註冊發送事件或接收事件,使得全部組件均可以上下平行地通知其餘組件,可是若使用不慎,就會形成難以維護的「災難」。

Vue是單頁應用,若是你在某一個頁面刷新了以後,與之相關的 EventBus 會被移除,這樣就致使業務走不下去。還要就是若是業務有反覆操做的頁面, EventBus 在監聽的時候就會觸發不少次,也是一個很是大的隱患。這時候咱們就須要好好處理 EventBus 在項目中的關係。一般會用到,在vue頁面銷燬時,同時移除 EventBus 事件監聽。

移除EventBus 事件監聽的方法以下:

// 移除指定事件
this.$EventBus.$off('changeCount')

// 移除全部事件
this.$EventBus.$off()
複製代碼

$parent與$children

使用範圍

經過插槽嵌套的父子組件進行事件調用和傳值。

使用方法

App.vue

<template>
  <div id="app">
      <component3>
          <component4></component4>
      </component3>
  </div>
</template>

<script>
import Component3 from "@/components/Component3";
import Component4 from "@/components/Component4";

export default {
    name: 'App',
    components: {
        Component3,
        Component4,
    }
}
</script>
複製代碼

父組件

<template>
    <div class="component3">
        <div class="title">Component3</div>
        <!-- 獲取到子組件的text1值 -->
        <div>
            <span class="text-bold">text:</span>
            {{text2}}
        </div>
      
        <div>
            <span class="text-bold">count:</span>
            {{count}}
        </div>
        <!-- 增長子組件的count值 -->
        <button @click="addChildrenCount">
            add count of Component4.vue
        </button>
      
        <!-- 插槽 -->
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: "Component3",
    data() {
        return {
            text1: 'Text From Component3.vue',
            text2:'',
            count: 0
        }
    },
    mounted() {
        // 獲取子組件的text1值
        this.text2 = this.$children[0].text1;
        // 監聽子組件調用changeCount,並將參數value賦值給父組件的count
        this.$on("changeCount",value => this.count = value);
    },
    methods:{
        addChildrenCount(){
            // 調用第一個子組件綁定的addCount方法
            this.$children[0].$emit('addCount');
        }
    }
}
</script>
複製代碼

子組件

<template>
    <div class="component4">
        <div class="title">Component4</div>
        <!-- 獲取到父組件的text1值 -->
        <div>
            <span class="text-bold">text:</span>
            {{text2}}
        </div>
      
        <div>
            <span class="text-bold">count:</span>
            {{count}}
        </div>
      
        <div>
            <input type="number" placeholder="Number..." v-model="value">
        </div>
        <!-- 修改父組件的count值 -->
        <button @click="changeParentCount">
            change count of Component3.vue
        </button>
    </div>
</template>

<script>
export default {
    name: "Component4",
    data() {
        return {
            text1: 'Text From Component4.vue',
            text2:'',
            count: 0,
            value:''
        }
    },
    mounted() {
        // 獲取父組件的text1值
        this.text2 = this.$parent.text1;
        // 監聽父組件調用addCount,並將count++
        this.$on('addCount',() => this.count++);
    },
    methods:{
        changeParentCount(){
            // 調用父組件綁定的changeCount方法,並傳入value參數
            this.$parent.$emit('changeCount',this.value)
        },
    }
}
</script>
複製代碼

效果

Kapture 2020-10-13 at 18.14.55.gif

注意

$refs$parent$children 是組件渲染後纔會進行填充,所以沒法在計算屬性 computed 使用。

$children 返回的是一個數組,一個父組件可能有多個子組件,但一個子組件只能有一個父組件。

provide與inject

使用範圍

父組件向任一後代組件傳值和方法,適用於正常嵌套和插槽嵌套。

使用方法

App.vue

<template>
  <div id="app">
      <component3>
          <component4>
              <component5></component5>
          </component4>
      </component3>
  </div>
</template>

<script>
import Component3 from "@/components/Component3";
import Component4 from "@/components/Component4";
import Component5 from "@/components/Component5";

export default {
    name: 'App',
    components: {
        Component3,
        Component4,
        Component5,
    },
}
</script>
複製代碼

父組件

<template>
    <div class="component3">
        <div class="title">Component3</div>
        <div>
            <span class="text-bold">count:</span>
            {{count}}
        </div>
        <!-- 插槽 -->
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: "Component3",
    // 向後代組件提供text和執行上下文this
    provide(){
        return{
            text:this.text1,
            component3:this
        }
    },
    methods:{
        addCount(){
            this.count++;
        }
    }
}
</script>
複製代碼

子組件

<template>
    <div class="component4">
        <div class="title">Component4</div>、
        <!-- 顯示父組件的text值 -->
        <div>
            <span class="text-bold">text:</span>
            {{text}}
        </div>
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: "Component4",
    // 接收父組件的text值
    inject:['text']
}
</script>
複製代碼

孫組件

<template>
    <div class="component5">
        <div class="title">Component5</div>
        <!-- 顯示祖組件的text值 -->
        <div>
            <span class="text-bold">text:</span>
            {{text}}
        </div>
        <!-- 增長祖組件的count值 -->
        <button @click="addGrandParentCount">
            add count of Component3.vue
        </button>
    </div>
</template>

<script>
export default {
    name: "Component5",
    // 接收祖組件的text值和執行上下文
    inject:['text','component3'],
    methods:{
        // 經過祖組件執行上下文調用其方法
        addGrandParentCount(){
            this.component3.addCount();
        }
    }
}
</script>
複製代碼

效果

Kapture 2020-10-13 at 18.31.59.gif

$attrs與$listeners

使用範圍

孫組件獲取祖組件的值或調用祖組件事件。

使用方法

祖組件

<template>
    <div class="component6">
        <div class="title">Component6</div>
        <div>
            <span class="text-bold">count:</span>
            {{count}}
        </div>
                
        <!-- 祖組件向本身組件傳遞了text1與text2和一個addCount事件 -->
        <component7 :text1="text" :text2="text" @addCount="addCount" />
    </div>
</template>

<script>
import Component7 from "@/components/Component7";

export default {
    name: "Component6",
    components:{
        Component7
    },
    data() {
        return {
            text: 'Text From Component6.vue',
            count: 0
        }
    },
    methods:{
        addCount(){
            this.count ++
        }
    }
}
</script>
複製代碼

父組件

<template>
    <div class="component7">
        <div class="title">Component7</div>
        <div>
            <span class="text-bold">text:</span>
            {{text1}}
        </div>
        <!-- 將不被父組件prop識別的attribute綁定到孫組件,也將祖組件的事件綁定到孫組件 -->
        <component8 v-bind="$attrs" v-on="$listeners" />
    </div>
</template>

<script>
import Component8 from "@/components/Component8";

export default {
    name: "Component7",
    // 父組件prop識別了text1,所以text1不會傳遞到孫組件
    props:{
        text1:{
            type:String
        }
    },
    components:{
        Component8
    }
}
</script>
複製代碼

孫組件

<template>
    <div class="component8">
        <div class="title">Component8</div>
        <div>
            <span class="text-bold">text:</span>
            {{text2}}
        </div>
        <button @click="addGrandparentCount">
            add count of Component6.vue
        </button>
    </div>
</template>

<script>
export default {
    name: "Component8",
    // 孫組件獲取到祖組件的text2
    props:{
        text2:{
            type:String
        },
    },
    methods:{
        // 孫組件調用了祖組件的addCount事件
        addGrandparentCount(){
            this.$emit('addCount');
        }
    }
}
</script>
複製代碼

效果

Kapture 2020-10-13 at 20.34.35.gif

注意

$attrs 包含了父做用域中不做爲 prop 被識別 (且獲取) 的 attribute 綁定 (class 和 style 除外)。所以當 text1 已被子組件prop獲取後,在孫組件是獲取不到 text1 的。

相關文章
相關標籤/搜索