鐵人賽至今也過半了,所以為了慶祝一下,我們最後來講講各種 Vue App 之間的溝通方法。扣除掉你可能是使用 Webpack 來封裝你的 App 之外,我們也會提及在不同的封裝之間,我們的 App 要如何來進行溝通。


App 與 App

我們之前有提過 new Vue 之間的一些狀況,

new Vue v.s new Vue Day 7

當我們在同一個封裝過程中,這兩個 App 其實還是分屬於不同的執行個體。所以,兩個不同的 App 之間,我們原本可以使用的一些事件溝通方式,在這邊當然就無法使用。所以,根據 上一篇 所提及的 EventBus 的方法,我們可以在外部先製作一個 EventBus 來進行溝通。

import Vue from 'vue'
import App1 from './App1.vue'
import App2 from './App2.vue'
import EventBus from '@/extends/OldDriver.js'

new Vue({
  created () {
    EventBus.$on('App1Created', () => {
      // App 1 建立囉~
    })
  },
  render: h => h(App1)
}).$mount('#app1')

new Vue({
  created () {
    EventBus.$on('App2Created', () => {
      // App 2 建立囉~
    })
  },
  render: h => h(App2)
}).$mount("#app2')

這麼一來,你在其他的元件內部,只要一樣使用 EventBus 就能跨出 App 來做事件傳遞。這個先決條件就是,你的整套應用程式,必須要在 同一個 Webpack 的封裝下,才能正常運作。

當然,如果你想要使用 Vuex 來做資料傳遞,只要兩個 App 都使用同一套 Store 的話,這麼做也是可行的。

import Vue from 'vue'
import App1 from './App1.vue'
import App2 from './App2.vue'
import Store from './stores'

new Vue({
  Store,
  render: h => h(App1)
}).$mount('#app1')

new Vue({
  Store,
  render: h => h(App2)
}).$mount("#app2')

這麼一來,你的兩個 App 就能共用同一組狀態管理器,當然,跟事件傳遞一樣,你必須自己釐清各種觸發狀態以及資料更新的時機,還有關於生命週期的事情。

Vuex 基本入門 Day 8

所以,上述的方法都還算是能夠解決各種溝通的面向。但是,會不會有失靈的時候?撇除掉你使用 Vuex 來做資料交換,事件傳遞是不是真的使用 EventBus 就是萬解呢?


EventBus 失靈

上一個段落提及了 同一個 Webpack 的封裝下,才能正常運作。那麼,為何會有 EventBus 失靈的狀況?是有的,在你不使用 Webpack,爾或者是你使用不同的 Webpack 程序來封裝的時候。關於使用不同的封裝,我們後續在動態載入的時候會再詳細提及,這裡先知道有這件事情就好。

所謂的不同封裝,你可以想像成有兩個 Vue App,但是他跑在兩個不同的應用程序結構下。所以,你以為的 new Vue() 就不會是同一件事情。所以,即便他們看起來都叫做 EventBus,但是在封裝過後,兩個不同的 App 所封裝出來的 EventBus 就會分處於不同的應用範圍( scope )中,所以,這兩個 EventBus 不會互通有無也是很合理的。

import Vue from 'vue'
import App1 from './app1/App.vue'
import EventBus from '@/app1/extends/OldDriver.js'

new Vue({
  created () {
    EventBus.$on('App1Created', () => {
      // App 1 建立囉~
    })
  },
  render: h => h(App1)
}).$mount('#app1')
import Vue from 'vue'
import App2 from './app2/App.vue'
import EventBus from '@/app2/extends/OldDriver.js'

new Vue({
  created () {
    EventBus.$on('App2Created', () => {
      // App 2 建立囉~
    })
  },
  render: h => h(App2)
}).$mount('#app2')

你覺得上面兩個 App 所使用的 EventBus 會是同一台車嗎?這種狀況,即便你使用 Vuex 也是兩套 不一樣 的狀態管理器,亦即,你的 Store 不是我的 Store,所以就算是這樣,你還是無法共用。


在這種情況下,我們該怎麼把事件傳遞處理的 比較好 呢?另外一種處理思維是,我們將這個是件傳遞的工具,往上交給 window 來做,亦即:

window.EventBus = new Vue()

這麼一來,無論我是否使用相同的 Webpack 封裝,我只要使用 window.EventBus 就能夠 上同一台車 呼叫到我所指定過的事件,也能互相傳遞資訊了。

同樣的情況,你也可以把 Vuex 往上提升到最外層( window )。換句話說,即便你分開封裝兩套 App,只要 Vuex 都能正確引入,那麼你還是可以使用同一套狀態管理器來操作的。

import Vue from 'vue'
import App1 from './app1/App.vue'

new Vue({
  window.Store,
  render: h => h(App1)
}).$mount('#app1')
import Vue from 'vue'
import App2 from './app2/App.vue'

new Vue({
  window.Store,
  render: h => h(App2)
}).$mount('#app2')

只是這樣會增加應用程式的複雜度,而且絕大多數的應用情境裡,可能也不會這樣操作。但是背骨如我,我們後續在動態載入的時候會告訴你這種奇怪的操作方式。


window 好嗎?

不好但是可以接受(結案)。

你必須意識到一件事情,所謂的 window 是指什麼?

  • 對於 JavaScript 來說是真正的 全域
  • 你的事件、狀態控制器都會 暴露 在最外面。
  • 失去了作用域的保護。
  • 只要有心,人人都可以竄改你的事件、狀態控制器內的資料。

Chrome DevTool 是一個好東西, 每個人都應該要有

你必須清楚知道這樣做的風險是什麼,不然是相當不建議這樣操作的。或許我後面在動態載入的時候會提及 window 這件事情,不過,對於事件傳遞,甚至是狀態控制這些事項,倘若你有較為敏感的資訊,例如登入的使用者資料,那麼就非常不建議使用這種方式。

除非,你的系統是公司內部使用,然後內部控管做得非常好。

不然就是 App 做好了不要上線(選我正解)。

當然,為了安全性,你也可以使用一些簡單的查核機制,來確保你的事件傳播以及狀態控制器被 呼叫 的合法性。不過這件事情真的就是只能防君子,不防小人。畢竟你知道的,即便你的 JavaScript 已經壓縮過了,我們還是可以找到你想要做的那些事情( 不要問,很可怕 )。


小結

這個章節真的比較畸形,其實你不太會有機會分開打包你的 App,但你知道的,我就是會做這種事情的人。不然鐵人賽就沒什麼特別的東西可以寫了啊(燦笑)。

ITHome 鐵人賽同步刊登 Vue App 的溝通方式 Day 15