EventBus & Vuex?

前幾天有盆友在羣裏問,vue.$emit和$on能夠跨組件完成全局通訊,那豈不是能夠徹底代替vuex,爲啥還要用vuex呢。
這個問題就要從eventbu事件總線s和vuex起源提及了。
(趁着尤大的vue3.0還在路上,咱們來炒炒現飯)html

組件通訊

在好久好久之前,在Vue王國裏有一個組件塔,有一個組件家族,A,B,C,組件父親A和兒子B,C生活在不一樣的層級之間,他們相隔很是遠。vue

父->子

這個時候,父親想念兒子了,想給他寄信,怎麼辦呢?簡單!在每個子組件上都有一個props 「郵箱」,經過這個郵箱,父親能夠直接投遞郵件(data)vuex

Vue.component('blog-post', {
      // 在 JavaScript 中是 camelCase 的
      props: ['postTitle'],
      template: '<h3>{{ postTitle }}</h3>'
    })
    <!-- 在 HTML 中是 kebab-case 的 -->
    <blog-post post-title="我是父親的消息!"></blog-post>
複製代碼

子->父

收到父親來信後,B,C都很是高興。想給父親回話,這時白鴿使者vue.$emit出來了,子組件用vue.$emit傳遞消息,往上層傳播。
json

this.$emit("todo",{
    res:"我是兒子的消息"
})
複製代碼

接到消息後,父親得對兒子傳遞過來的不一樣類型信息作出不一樣的迴應bash

this.$on('todo',function(data){
        // todo
        //data => 來自兒子的消息
    })
複製代碼

兄弟

A常常教育B和C,說兄弟之間要多聯繫,增長感情,可是$emit又只能向上層傳遞消息,住在同一層的兄弟兩又該怎麼聯繫呢?app

eventbu事件總線

這時候有一個智者想到了一個方法,既然大家都用$emit這個方法,我何不直接開個順豐快遞呢? 這時候vue eventbus事件總線出來了,利用一個遊離在組件塔外的vue實例,構造了一個bus總線,兒子的消息經過$emit給bus總線.ide

B.$root.bus.$emit('todo')
複製代碼

這時候父親所獲得的消息是由bus總線發出的,並設置響應事件post

A.$root.bus.on('todo',function(){})
複製代碼

這樣父親兒子就能夠輕輕鬆鬆的聯繫了
那最關鍵的問題是,兄弟之間怎麼聯繫呢?
總線的智者這時候跳出來講,顧客就是上帝,不論是父親兒子均可以使用咱們順豐快遞總線,這樣也就實現了兄弟組件之間的通訊ui

B.$root.bus.$emit('todo')
// 兄弟C也能收到這個消息
C.$root.bus.on('todo',function(){})
複製代碼

problem?

這個時候回到本文關鍵,爲何eventBus這麼好的東西咱們還要使用vuex呢?
如下內容參照 vuex-basics-tutorial
最開始,咱們的項目多是簡簡單單這樣的 this

在過了很長一段時間以後,王國不斷髮展,項目愈來愈大,組件塔裏的組件愈來愈多,他們長得也愈來愈複雜,整個王國變得亂七八糟,項目結構變成下面這個樣子。不一樣組件之間通訊愈來愈多,代碼段裏不斷的出現$emit和$on致使瞭如下幾個主要問題

1. 代碼邏輯性極具降低,可閱讀性變低
2. 對於每個action父組件都須要一個on(或dispatch)一個事件來處理
3. 你將很難查找到每個事件是從哪裏觸發,滿篇都是業務邏輯
複製代碼

簡單的例子

舉個簡單的例子。你的公司原來有一個display組件,他的做用是展示App根組件上increment的當前值。你新招了兩個新員工,給他們分配了兩個任務。

  1. 你讓小A開發一個新的計數器在一個新的組件(childDisplay)裏運用,實現點擊一次增長increment一次,並在組件內展示increment當前的數字。這個計數器訂閱了increment,點擊計數器成功讓display和childDisplay顯示increment增長1後的數字。
    小A開開心心迅速的完成了這個任務commit而後push了
  2. 你跟小B說,我須要一個按鈕組件,這個按鈕組件向app實例emit了一個reset事件,這個事件將重置App上increment爲0
    好的,小B也開開心心的commit而後push了
  3. 這個時候你會發現當你點擊A組件時,在display和childDiplay上都成功顯示了increment增長1之後的數字,但點擊B組件觸發reset事件時,根組件上的increment重置爲了0,但因爲小B並不知道A組件也訂閱了increment這個數據,致使A組件狀態沒有更新。

vuex

爲了防止這些問題,這個時候,國王尤小右站了出來,他藉助隔壁flux王國的思想,帶領全國的智者研究出了一個新的方式來解決這種混亂的問題。那就是vuex了。

按照定義來講

  1. store是一個倉庫,倉庫保存着項目的數據(store.state)
  2. (嚴格模式)爲了解決沒法追蹤狀態變化的問題,倉庫裏的數據不直接經過修改store.state來改變,而是經過commit一個動做告訴倉庫,將會報錯 [vuex] Do not mutate vuex store state outside mutation handlers
  3. 倉庫收到了commit的動做後,將會在store.mutation裏查找對應事件,並改變store.state

回到簡單的例子

解決方案1(簡單store模式)

爲了解決上述問題,咱們作出了幾個有意思的改變。(state)

  1. 在store裏定義了一個常量store
  2. 在本地數據裏定義了一個叫作sharedState的變量,是store.state裏的映射
  3. 由於sharedState是vue的數據,因此他讓store.state也變成了一個響應式數據,當store.state裏數據發生改變時,vue將會自動的更新sharedState裏的數據。

這樣,原有的問題就解決了,A組件和B組件訂閱的increment再也不是根組件上的數據了,而是倉庫的數據。當倉庫數據改變時,vue將自動更新到每一個訂閱了increment數據的組件上。

// 這是父級display組件
<template>
  Count is {{ sharedState.counter }}
</template>

<script>
import store from '../store'

export default {
  data () {
    return {
      sharedState: store.state
    }
  }
}
</script>
複製代碼
// 這是A組件
import store from '../store'
<template>
  Count is {{ sharedState.counter }}
</template>
export default {
  data () {
    return {
      sharedState: store.state
    }
  },
  methods: {
    activate () {
      this.sharedState.counter += 1
    }
  }
}
複製代碼
// 這是B組件
import store from '../store'
export default {
  data () {
    return {
      sharedState: store.state
    }
  },
  methods: {
    reset () {
      this.sharedState.counter = 0
    }
  }
}
複製代碼
方案1變形

上述的方式就是最好的了嗎?試想當A,B在這家公司開發了無數多個reset組件和計數器組件後離職了。這時候招來了一個新的員工C,你跟C說,我想要全部的increment大小不超過100,難道C要對全部組件進行重構,判斷大小嗎,這是很是荒謬的選擇。這時候咱們引入了新的方法。(mutation)

var store = {
  state: {
    counter: 0
  },
  increment: function () {
    if (store.state.counter < 100) {
      store.state.counter += 1;
    }
  },
  reset: function () {
    store.state.counter = 0;
  }
}

export default store
複製代碼

根據上面的衍生,vuex就慢慢成型了

vuex
咱們從新建立store.js

import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex)

var store = new Vuex.Store({
  state: {
    counter: 0
  },
  mutations: {
    INCREMENT (state) {
      state.counter ++
    }
  }
})

export default store
複製代碼

咱們看下這裏作了什麼樣的操做

  1. 咱們引入了一個vuex模塊,並做爲vue插件激活
  2. 咱們的倉庫store再也不是一個普通的jsonObject而是一個vuex.store的實例
  3. 採用了mutation,用於改變state

why better ?

  1. 若是在開發過程當中保留了全部狀態的副本,咱們能夠像super hero同樣如時空穿梭般對代碼進行調試,記錄每一次狀態的變化
  2. 你能夠構建一箇中間件,好比一個日誌打印器,在每次用戶提交一個action時記錄操做,當出現問題時你將會更加容易的調試與fix bug
  3. 強制要求你把全部action經過一個store管理,這樣團隊開發將會更加便捷與明瞭

總結

經過長篇的分析咱們能夠得出本文結論了。

  1. vuex是官方推出的,事件總線是高手在民間
  2. 在大型應用方面,vuex確實是一個比EventBus更好的解決方案
  3. vuex更加易於調試與管理
  4. Vuex並非最佳的解決方案,在某些小型應用上,你可能只有小部分的數據交互,甚至只有一個登陸狀態儲存,那樣事件總線或者簡單狀態管理都是值得推薦的。 最後說一句,vuex真香。 ps:這只是筆者的看法,有什麼問題歡迎你們踊躍提出指正。😘
相關文章
相關標籤/搜索