iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 14
0
Modern Web

VueJS 從前端到後端系列 第 14

Component 的溝通方式 Vuex Day 13

部落格同步刊登 [IT 鐵人賽] Component 的溝通方式 Vuex Day 13

我們會把 Vuex 放的比較前面,是因為既然使用了狀態管理機制,那麼,用他來跟各種元件溝通,是比較方便的一件事情。然後,我拖了那麼久才講到元件溝通好像有點奇怪,也是啦,其實我本來就不是很喜歡溝通(欸不對)。

是說,溝通只是 Vuex 附加的好處而已其實。


元件交換資料

我們之前有提過元件之間的資料交換,也提過 Vuex 的基本應用:

之前說過 Vuex 是幫我們做狀態管理的,所以說我們拿來做元件交換資料也是很適切的事情。首先你知道 Vuex 有所謂的 state 提供我們放置資料,而 mutations, actions 拿來更新資料,然後 getters 用於取出。所以,我們每個元件當中,都可以利用這個方式來交換資料。

import { mapGetters } from 'vuex'

export default {
  name: 'AComponent',
  computed: {
    ...mapGetters({
      getUsers: 'users/get'
    })
  }
}
import { mapGetters } from 'vuex'

export default {
  name: 'BComponent',
  computed: {
    ...mapGetters({
      getUsers: 'users/get'
    })
  }
}

當然你把 mapGetters 換成 mapState 也是相通的。不過再次提醒一下,在嚴謹模式下,你不能針對 Getters 或是 State 取出來的資料作任何的更動。所以,你就只能通過 Mutations 或是 Actions 去更動他。

那麼我們在使用 Mutations 更動的時候,各個元件的 Getters 是不是真的更新了呢?

https://ithelp.ithome.com.tw/upload/images/20190915/20001433YlzLFxfbF6.png

我們有兩種方式去得知你的 Getters 有更新,其一是使用 updated 這個生命週期的勾子,其二是使用 watch 這個方法。從上述的例子,我們使用 watch 來監聽一個 user 的物件,使用 updated 來監聽元件更新。差異在哪?我們來看一個例子:

https://ithelp.ithome.com.tw/upload/images/20190915/20001433iVAg60ohnT.png

上面這個範例,我們維持 watch 的方式來監聽 getUser 這個資料,然後你會發現,怎麼 watch 沒有被觸發了?接著,我們底下使用 updated 來看看 getUser 發生了什麼事情。

https://ithelp.ithome.com.tw/upload/images/20190915/200014337WhXuUlAiH.png

我們從 updatedgetUser 拉出來看,會發現資料確實有更新。那麼,為何 watch 會沒有被觸發呢?關於 watch 的部分,由於某些緣故,你必須使用 deep 個的第三個參數才會有正常的反應,我們後續會有一個篇幅來談談 watch 這件事情。

有在關注我的部落格的人,應該有看過我嘴過 Vue 的 watch 才是。


確保資料同步

我們先撇除 watch 的毛病,我們可以利用 Getters, State 來確保兩個元件都可拿到同一份資料。當然,這一份資料必須要能確保已經沒有任何非同步操作。還記得 Actions 可以使用非同步操作嗎?

const store = new Vuex.Store({
  state: {
    user: {}
  },
  mutations: {
    setUser: function (state, user) {
      state.user = user
    }
  },
  actions: {
    fetchUser: function (commit) {
      return axios({
        url: '/get_user'
      }).then(res => {
        commit('setUser', res.user)
      }).catch(() => {
        commit('setUser', {})
      })
    }
  },
  getters: {
    getUser: function (state) {
      return state.user
    }
  }
});

如果你在元件 A 當中呼叫了 fetchUser,而同時 B 元件使用 Getters 去讀取 user 的時候,你就有機會會因為非同步傳輸的狀況,導致兩者的資料不相同。所以說,倘若是你使用 watch 或是 updated 來監聽,就會有一點點 延遲 的狀況。

所以說,倘若你有很多個元件,倘若需要確保資料同步或是資料呈現一致,你需要留意幾件事情:

  • 注意 updatedwatch 延遲。
  • 可以搭配 EventBus 來做全域通知。
  • 利用特定 flag 來阻斷同一個資料的 連續 操作。

延遲的事情就如同剛剛提及的非同步傳輸造成,而 watch 衍生的事情我們後續會再聊聊。除了這些,你也是可以搭配 EventBus 來做通知,換句話說,當你的 Mutations 或 Actions 在做 commit 或是 dispatch 的時候,可以同時觸發一個 Event 讓你的元件及時更新。

至於 阻斷連續操作 這件事,你就把他想成,當我想要收藏某一個貼文,或是我們去臉書按讚的時候,是否允許連續操作。像是這種狀況,我會有兩種作法:

  1. 第一次按下「讚」,立一個 flag 阻斷他,直到 XHR 返回。
  2. 第 N 次按下「讚」,只會送出最後一次的請求,然後等 XHR 返回。

無論你採用什麼方式,最後都還是會有 Getters 或是 States 要等 XHR 回來的狀況,這些時候你就必須小心處理。


又是薛丁格

就如同上述的例子,除了你可能看不到變更,但是好像又有變更,將他使用 console.log 印出來他又真的有變更。

薛丁格甲賽啦!

多數狀況是所謂的「時機點」的問題,就如同上個段落所提及的,使用 EventBus 來做一個「通知」,這樣的動作就是強迫觸發一個事件,讓這個事件再次去提取你所想要的資訊。這樣你就能肯定在事件觸發的同時,資料已經有所變動。

如果事件觸發後資料沒改變,那一定是你自己的問題!
不要老是叫薛丁格去甲賽!

我們其實在元件趨於複雜時,對於 Vuex 的設定也會過於冗長,除了妥善利用 namespaced 來適時的區格外,資料結構相對的也需要妥善規劃。以下提出一些實作上的建議給大家參考:

  • 請不要在 state 裡面有資料關連。
  • 當你的資料是一個物件( Object )時,請留意更新方式:
    • 你是要更新 一個 還是 全部
    • 特別會更新 某個 屬性資料時,建議另外寫。
    • 若要更新 全部 請確保你的資料不會遺漏。
  • 當你的資料是一個陣列時,請留意更新方式:
    • 請注意 index 的變化。
    • 換掉 一個 或是 全部 ,你的 index 可能都不會變。
    • 注意型別,在 JavaScript 裡面他也叫做 object
  • 請留意 state 用於 computed 時,Render Function 的告警:
    • 無相關屬性,在渲染時會有警告或錯誤,請確保資料屬性存在。
    • 同上述,更新 全部 資料,請確保資料屬性一致性。
    • 預設值給好給滿,一生平安,出門撿到錢。

很多時候你必須要不斷的試錯才會發現一些問題,但這有時候有很難歸咎於他是個問題。只能說這些事情變成了各種工具、套件或是框架本身的「特性」而不能算是個錯誤或是什麼的。

至少這邊我有先踩到了一些看起來很蠢的雷,你們就不會再犯了。


小結

其實我沒有講很多「溝通」的事情。對於 Vuex 來說溝通真的只是附加價值,但是他也是能有效的在各種元件中暢行無阻。也不會有父元件、子元件的問題,也沒有事件傳遞的問題。

啊,對了,父元件、子元件與事件傳遞的問題,我們下一篇會提到。


上一篇
薛丁格的生命週期 Day 12
下一篇
Component 的溝通方式 :props Day 14
系列文
VueJS 從前端到後端31

尚未有邦友留言

立即登入留言