Vue從甜小白到皮大佬系列(六) 元件通訊

極客James發表於2019-08-30

? Vue構建大型單頁面電商應用 開源啦!點我看原始碼??

閱讀時間預估:8分鐘

元件之間通訊是Vue中最常用,最基礎的部分,通常元件之前的通訊分為以下幾種:

  • 1.父元件給子元件傳遞值,傳遞事件
  • 2.子元件給父元件傳遞值,傳遞事件
  • 3.兄弟元件之間傳遞值,傳遞事件

方法一、props $emit v-on

1.父元件給子元件傳值

父元件A通過props向子元件B通訊

接下來我們通過一個例子來加深以上理解:

自定義一個Son.vue子元件,引入到父元件中 在父元件中定義一個資料

<template>
    <div id="app">
        <!-- 前者自定義名稱便於子元件呼叫,後者要傳遞資料名 -->
        <son v-bind:user="user"></son>  
    </div>
</template>
複製程式碼
<script>
import Son from './components/Son'

export default {
    name: 'app',
    components: {
        Son,
    },
    data () {
        return { // 父元件定義資料,傳遞給子元件
            user: ["james", "alice", "joho"]
        }
    },
  }
</script>
    
複製程式碼

子元件中通過prop來定義父元件傳值的值型別,是否是必須以及預設值

<script>
export default {
    components: {

    },
    data () {
        return {

        };
    },
    computed: {

    },
    methods: {
    },
    props: {
        user: { //這個就是父元件中子標籤自定義名字
            type: Array,
            required: true,
            default: []
        }
    },
}
</script>
複製程式碼

然後在適當的位置將父元件傳遞過來的資料進行渲染

<template>
    <div>
        <ul>
            <li v-for="(item,index) in user"
                :key="index">姓名:{{item}}</li>
        </ul>

    </div>
</template>
複製程式碼

瀏覽器開啟會顯示user裡面的值.

總結:父元件通過props向下傳遞資料給子元件。注:元件中的資料共有三種形式:data、props、computed

2.子元件向父元件傳值(通過自定義事件形式)

核心點

  • 1.子元件使用this.$emit('fn',param)向父元件傳值

  • 2.父元件通過v-on:fn="fn"來接受子元件傳遞過來的值

子元件中:

子元件包含一個點選事件

// 定義一個點選事件
 <son v-bind:user="user"
             @titleChange="changeTitle">
</son>
複製程式碼

在點選事件中通過this.$emit()來傳遞事件

<script>
//import x from ''
export default {
    components: {

    },
    data () {
        return {
            title: '我是子元件',
            toParentData: '我是子元件傳遞的資料'
        };
    },
    computed: {

    },
    methods: {
        btnClick () {
            this.$emit('titleChange', this.toParentData);
        }
    },
    }
</script>

複製程式碼

父元件中: 通過v-on來接受子元件發出的事件名稱:titleChange並且和自己的changeTitle事件繫結

<template>
    <div id="app">
        <!-- 前者自定義名稱便於子元件呼叫,後者要傳遞資料名 -->
        <son v-bind:user="user"
             v-on:titleChange="changeTitle"></son>
        <p>{{msg}}</p>
    </div>
</template>
複製程式碼

實現changeTitle方法,改變data中的msg資料

<script>
import Son from './components/Son'

export default {
    name: 'app',
    components: {
        Son,
    },
    data () {
        return {
            user: ["james", "alice", "joho"],
            msg: '我是父元件顯示的內容'
        }
    },
    methods: {
        changeTitle (title) {
            this.msg = title;
        }
    }
}
</script>
複製程式碼

總結:子元件通過events給父元件傳送訊息,實際上就是子元件把自己的資料傳送到父元件。

方法二 中央事件匯流排 Bus 進行通訊

發現了一個問題,如果孫子元件給爺爺元件傳值,通過props或者$emit方式是一件很痛苦的事情,需要通過中間的爸爸來做銜接,這樣通訊顯然會使得元件耦合,同時兄弟元件間的通訊props,$emit也實現不了?那麼問題來了,如何解決呢?

這裡介紹中央事件匯流排的方式,名字高大上其實就是抽一個公共Vue例項媒介來管理,需要通訊的元件都引入Bus,之後通過分別觸發和監聽 Bus 事件,進而實現元件之間的通訊和引數傳遞.

  • 1.首先建 Vue 例項作為匯流排:
// Bus.js
import Vue from 'vue'
export default new Vue;
複製程式碼
  • 2.需要通訊的元件都引入 Bus.js,使用 $emit傳送資訊:
// ComponentA.vue
<template>
  <div>
    <b>元件A:</b><button @click="handleBus">傳遞數值給需要的元件</button>
  </div>
</template>

<script>
  import Bus from './bus.js' 
  export default {
    methods: {
      handleBus () {
        Bus.$emit('someBusMessage','來自ComponentA的資料')
      }
    }
  }
</script>
複製程式碼
  • 3.需要元件A資訊的就使用$on來監聽:
// ComponentB.vue
<template>
  <div>
    <b>元件B:</b><button @click="handleBus">接收元件A的資訊</button>
    <p>{{message}}</p>
  </div>
</template>

<script>
  import Bus from './bus.js' 
  export default {
    data() {
      return {
        message: ''
      }
    },
    created () {
      let that = this // 儲存當前物件的作用域this
      Bus.$on('someBusMessage',function (data) {
        that.message = data
      })
    },
    beforeDestroy () {
      // 手動銷燬 $on 事件,防止多次觸發
      Bus.$off('someBusMessage', this.someBusMessage)
    }
  }
</script>
複製程式碼

中央匯流排優點:可以解耦元件,方便兄弟間通訊

方法三 $parent / $children & $refs

  • $parent / $children:指定已建立的例項,在兩者之間建立父子關係。子例項可以用 this.$parent 訪問父例項,子例項被推入父例項的 $children 中。

  • $refs:一個物件,持有註冊過 ref特性的所有 DOM 元素和元件例項。ref 被用來給元素或子元件註冊引用資訊。引用資訊將會註冊在父元件的 $refs 物件上。

  • 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子元件上,引用就指向元件。

父元件

<template>
    <div id="app">
        <!-- 前者自定義名稱便於子元件呼叫,後者要傳遞資料名 -->
        <p>{{msg}}</p>
        <child-one ref="childOne"></child-one>
        <child-two ref="childTwo"></child-two>
        <button @click="oneContent">顯示child-one傳過來的資料</button>
        <button @click="twoOneContent">顯示hild-two傳過來的資料</button>

    </div>
</template>

<script>
// 引入子元件
import ChildOne from './components/ChildOne'
import ChildTwo from './components/ChildTwo'
export default {
    name: 'app',
    components: {
        ChildOne,
        ChildTwo
    },
    data () {
        return {
            msg: '我是父元件',
            content: 'James' //子元件要用的資料
        }
    },
    methods: {
        // button 的事件響應 
        oneContent () {
            const childOne = this.$refs.childOne;
            this.msg = childOne.msg;
        },
        twoOneContent () {
            // 通過$refs取到childTwo
            const childTwo = this.$refs.childTwo;
            this.msg = childTwo.msg;
        }
    },
}
</script>
複製程式碼

子元件1

<template>
    <div>
        <p>{{title}} 我的父元件是{{content}}</p>
    </div>
</template>

<script>
//import x from ''
export default {
    components: {
    },
    data () {
        return {
            content: '',
            title: '我是子元件 childOne',
            msg: '我只子元件childOne hello ereryOne'
        };
    },
    mounted () {
        // 從父元件裡取contet的資料
        this.content = this.$parent.content;
    },
}
</script>
複製程式碼

子元件2:

<template>
    <div>
        <p>{{title}} 我的父元件是{{content}}</p>
    </div>
</template>

<script>
//import x from ''
export default {
    data () {
        return {
            content: '',
            title: '我是子元件 childTwo',
            msg: '我是子元件childTwo hello ereryOne'
        };
    },
    mounted () {
        // 載入父元件content的內容
        this.content = this.$parent.content;
    },
}
</script>
複製程式碼

方法四: 釋出訂閱模式

官方GitHub

  • 1.通過npm install pubsub-js --save的方式引入pubsub庫
  • 2.建立event-type.js定義Symbol型別的資料並匯出
// event-types.js
export const MY_TOPIC = Symbol('MY_TOPIC')
複製程式碼
  • 3.在需要釋出(傳遞事件)的地方引入pubsubevent=type.js,並且通過以下方式釋出事件
import PubSub from 'pubsub'
import { MY_TOPIC } from './event-types.js'
PubSub.publish(MY_TOPIC, 'world');

複製程式碼
  • 4.在需要訂閱(接受事件)的地方引入pubsubevent=type.js,並且通過以下方式接受事件
import PubSub from 'pubsub'
import { MY_TOPIC } from './event-types.js'
PubSub.subscribe(MY_TOPIC, function (msg, data) {
	console.log(data)
});
複製程式碼

方法五 Vuex

官方傳送門

Vuex 是Vue官方推薦的一種解決元件中通訊的完美解決方案,也是在專案開發中必用的方案:

點我學習Vuex

認真看完本篇後對照思維導圖可以在腦海裡回顧一遍哦,對此塊的知識點你會有更深的認識和理解.

Vue元件思維導圖

如果我的分享對面前的這位大俠有所啟發,請不要吝嗇手中大拇指,以程式設計師最高禮遇點贊✨ 評論加分享的方式鼓勵我持續分享,也歡迎各位大佬勘誤,提出寶貴意見.

關注公眾號回覆:學習 領取前端最新最全學習資料,也可以進群和大佬一起學習交流

Vue從甜小白到皮大佬系列(六)  元件通訊
猛戳 我的前端進階Blog

相關文章