Vue 组件通信的解决方案

2,072 阅读4分钟

数据通信

首先, 我们通常说数据传递, 组件通信什么什么的, 我认为可以分成两种场景:

  • 页面和页面之间

  • 组件和组件之间

通信方案

不管什么场景, 在使用 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 是状态管理, 不是保存常量的地方.