使用全域性 bus 在 Vue 的兄弟元件中簡單傳值(以及 this 重定向的 5 種方法)

zhaiduting發表於2019-01-01

匯流排傳值有兩種寫法:原型bus寫法【 window.Vue.prototype.bus = new Vue() 】、全域性bus寫法【 window.bus=new Vue() 】。
如果使用了原型寫法,那在元件裡面必須使用 this.bus.$emit() 傳送訊息,以及使用 this.bus.$on() 接收訊息。它們全部要用 this 開頭。如果使用了全域性寫法,則可以簡化為 bus.$emit() 及 bus.$on() 了。

在確保全域性bus不會發生變數名稱衝突的前提下,使用全域性bus的寫法要更簡單一些。首先,修改 resources\assets\js\app.js 檔案引入全域性bus

require('./bootstrap');
window.Vue = require('vue');
window.bus=new Vue();   //全域性bus
// window.Vue.prototype.bus = new Vue();   //原型bus
import ...
const app = new Vue({
    el: '#app',
    components:{
        ...
    },
});

這裡需要注意的是window.bus=new Vue() 這行程式碼的位置有講究,不能把它放在 const app=new Vue({...}); 之後。

在blade模板中,以下兩個Vue元件是同級關係

<cart-items :items="{{$cartItems}}" ></cart-items>
<cart-selected></cart-selected>

file
元件1的作用:
1、接收資料庫查詢到的複雜物件 $cartItems
2、根據使用者的勾選或刪除等操作進行加工處理,得到特定資料data
3、加工完畢,傳送一個訊息msg_SelectedItemChange 並對外傳遞資料data
元件1傳送資料的程式碼很簡單,只需一行
bus.$emit('msg_SelectedItemChange', data)

元件2的作用:
1、接收元件1加工後的資料data
2、將此資料重新整理到瀏覽器裡(或者等待使用者點選提交按鈕傳送ajax請求等等)
元件2接收資料稍微麻煩一點,因為需要使用一個回撥函式,大致如下
bus.$on('msg_SelectedItemChange', 回撥函式的名稱)

難點在於回撥函式內部的this指向可能有問題,比如下面的寫法就不能在瀏覽器裡面顯示元件2獲取的資料

<template>
    <div>元件2:{{cartItems}}</div>
</template>
<script>
    export default {
        name: "CartSelected",
        data(){
            return{
                cartItems:[],
            }
        },
        mounted(){
            bus.$on('msg_SelectedItemChange', function(data){
                this.cartItems=data;    //無效,因為this指向的已經不是本元件了!
            })
        },
    }
</script>

嘗試使用call()重定向回撥函式內部的this指向也是無效的

        mounted(){
            bus.$on.call(this, 'msg_SelectedItemChange', function(data){
                this.cartItems=data;    //也無效
            })
        },

改用that重定向,有效!

        mounted(){
            let that=this;
            bus.$on('msg_SelectedItemChange', function(data){
                that.cartItems=data;    //有效,匿名函式使用that重定向到本元件
            })
        },

改用立即執行函式並返回一個閉包函式的寫法也是有效的,可惜晦澀得很,寫起來也麻煩。閉包函式既能訪問形參it又能訪問that變數,使用任何一個都可以完成任務,唯獨不能使用this

        mounted(){
            let that=this;
            bus.$on('msg_SelectedItemChange', function(it){
                return function(data){
                    that.cartItems=data;    // OK
                    it.cartItems=data;      // OK too
                    this.cartItems=data;    // Bad
                }
            }(this))
        },

使用bind重定向(很簡潔的寫法)
雖然that重定向的寫法也很簡潔,但是函式體裡面的this必須全部改用that替換。使用bind則無需替換,剛好解決了這一缺陷。

        mounted(){
            bus.$on('msg_SelectedItemChange', function(data){
                this.cartItems=data;
            }.bind(this))
        },

也可以採用一種很標準的寫法(往methods物件裡面插入一個新方法)。此法不夠簡潔,但是比較靈活,可以隨時呼叫refresh()方法。

        methods:{
            refresh(data){
                this.cartItems=data;
            }
        },
        mounted(){
            bus.$on('msg_SelectedItemChange', this.refresh)
        },

下面是第5種,使用箭頭函式的寫法(比bind還要簡潔)

        mounted(){
            bus.$on('msg_SelectedItemChange', data=>
                this.cartItems=data
            )
        },

相關文章