Vue 元件通訊的解決方案

雲水搖啊搖發表於2017-07-12

資料通訊

首先, 我們通常說資料傳遞, 元件通訊什麼什麼的, 我認為可以分成兩種場景:

  • 頁面和頁面之間

  • 元件和元件之間

通訊方案

不管什麼場景, 在使用 Vue 的時候, 一般我們有下面 5 種選擇去實現資料通訊.

  1. vuex

  2. storage

  3. props

  4. event

  5. URL queryString

選擇通訊方案

我們在選擇通訊方案的時候, 比如說確定 列表頁如何把每一項的 id 傳遞給 詳情頁的時候,
一般要考慮什麼問題? 你是直接全套都是 vuex, 還是說喜歡使用 sessionStorage?

一般我們要考慮下面的幾個問題:

  1. 頁面是否可以重新整理

  2. 頁面是否可以分享 (或者說URL 是否要求 RESTful)

  3. 資料更新之後, 所有使用此資料的元件是否都需要響應更新

分析

先說 `頁面和頁面之間的通訊場景`, 首先上面的 5 種方案, 我們可選的有:

vuex, storage, URL queryString.

然後分析一下, 每一種方案, 它對上面的 3個問題, 是不是很好的解決掉了:
備註:
頁面通訊場景不會要求實時響應, 因為就算下個頁面的確是實時響應, 你也看不見,
所以主要看 `重新整理` 和 `分享`

vuex: 不能重新整理, 不能分享
storage: 不能分享
url: 能重新整理, 能分享

這樣看來, url queryString 的方式是 `頁面通訊場景` 中的最佳選擇, 但是我依舊有疑慮:

  1. 我始終覺得把跳轉資訊, 暴露給使用者, 是很不好的事情; (心理問題, 可以克服)

  2. url 的長度限制; 這個無所謂的, 2k, 你再怎麼傳遞, 我都不會覺得你會出現超過 2k 的情況

  3. url 需要拼接, 這個拼接是否麻煩? 也不麻煩, 只是物件轉字串.

  4. 這樣每個頁面都需要在進入的時候先解析一下 queryString, 這樣是不是增加了麻煩的程度
    也可以通過 mixins 來操作. 聚合到 mixins, 況且也不一定很多.

所以我們可以選擇 `url queryString` 作為 `頁面和頁面通訊場景` 中的通訊方案.
以後你就可以這樣用了:

比如列表頁面跳轉到詳情頁要帶一個 id

    this.$router.push({
        path: `detail`,
        query: {
            id
        }
    })

你的 url 會始終長這樣:

https://abc.com/#/?id=123

備註: 如果你的頁面不能重新整理和分享, 你完全可以三種方案隨便選, 愛誰誰.

重點: url queryString 的方式, 有一個問題解決不了:

從詳情頁到訂單頁, 通過 queryString 帶了商品資訊過來, 假設此時 url 長這樣:
    order/?goods=xxx
訂單頁面有一個收貨位址列, 點選可以進入地址編輯頁面, 此時的 url 不會帶引數的(你可以試試帶一下看多麻煩)
    address-edit/
地址編輯頁面有一個儲存按鈕, 點選會返回到訂單頁面
    order/

so, url queryString 丟了.

我目前的解決方案:
針對這種存在多入口的頁面, 一定要在進入它的第一時間, 先把 queryString 存起來.
並且做如下判斷:

if (// 存在 queryString) {
    // use queryString
} else {
    // use storage
}

但是這種方式還是搞不定 從地址編輯頁返回到訂單頁, 使用者此時分享訂單頁, 分享出去的玩意肯定會是錯的.


現在來說下 `元件和元件之間的通訊場景`
上面的 5 種方案, 可以選擇 vuex, event, props, storage

先看下 重新整理, 分享和實時響應
vuex, 不能重新整理
event, props 能重新整理能分享
storage 不能分享 & 實時.

解釋:
為什麼 vuex 在這裡還是不能重新整理
因為如果使用的 state 裡面的值是其他頁面設定的而不是 init 就存在的, 重新整理丟值.
為什麼 event, props 可以做到防重新整理防分享
因為這兩個玩意是程式執行它就生效的, 它也可以做到實時更新.
storage 雖然在存的時候有一個事件, 但是這太 trick 了.

所以我們選擇的是 event, props?

分析一下吧.
元件通訊可以分成兩種, 父子, 同輩.

父子之間呢:
父傳子: props
子傳父: $emit(event)

這就是 `props down, events up`;

但是其實還有:
父傳子: this.$refs.xxx
子傳父: this.$parent.xxx

還有: 自定義 v-model

還有: 讓 props 是一個物件.

同輩之間: event-bus.

所以這就完了? 啥都沒有了? 嗯, 就這樣.

思考

  1. 關於 vuex 的應用場景的考慮
    不是應該所有的元件, 路由之間的資料傳遞都應該通過 vuex, 當同時存在兩種方式可以選擇的時候,選擇 vuex 的唯一理由只有一個:

    需要響應式的狀態

    why?

    因為 vuex 雖然有輔助函式, 但是用起來還是要 引入, 定義. 而且真的是一重新整理頁面就掛了. 
  2. 可以通過監聽 beforeunload 事件, 在其中快取 state, 然後在 onload 事件再恢復, 這樣可以避免掉vuex 的丟值.

  3. 沒有必要追求全專案統一的一種通訊方式, 理論上你不考慮重新整理分享, 全專案都用 vuex, 什麼事情也不會有的.

  4. vuex 是狀態管理, 不是儲存常量的地方.

相關文章