Baurine's Blog

对单一输入源的理解

February 02, 2019

记录一下最近对单一输入源的理解。

最近在做的一个项目是这样的,一个音频播放网站,非 SPA,就像所有的音乐网站一样,所有页面的底部都有一个独立的音频播放器组件,在任意页面,如果有音频的播放按钮,点击这个按钮,播放器就会开始播放这个音频,并且显示这个音频的详细信息,比如 title, cover image, author。

之前的实现方案是,点击页面上的播放按钮后,会把当前此音频的所有信息通过 custom event dispatch 给完全独立的音频播放器,音频播放器通过 window.addEventListener() 监听这个 custom event,然后播放此新的音频并在界面上所示它的信息。

这样带来的问题是,在播放器组件,需要显示音频的 title/cover image/author,但在各个页面上,不一定显示这些信息,比如页面 A 只显示音频的 title,页面 B 只需要 cover image。但为了能在播放器组件上显示完整,在后端生成这个页面时不得不去获取音频的完整数据,虽然在页面上不需要显示出来。比如在访问页面 A,本来我只需要获取音频的 title,但实际我要去获取它的 title/cover image/author 等完整信息。

当页面多了以后,就会发生漏掉获取音频的某些属性,导致点击播放按钮后播放器崩溃,或者虽然不崩溃,但显示空白 (后者有时还不容发现)。

此时,音乐播放器面临输入源来自多个的问题,多个输入源可能导致状态/数据不一致。

然后我就想到了是不是可以把输入源单一化。

新的解决方案是这样的,在任意页面点击的播放按钮后,我只把当前音频的 id 通过 custom event 发送到音频播放器,然后音频播放器再通过音频的 id 访问 API,从 API 中拿到该音频的详细信息,然后显示和播放。

虽然多了一次网络请求,但它带来的好处不言而预,每个页面不再需要去获取多余的音频属性,音乐播放器只从 API 拿数据,能保证数据的完整性。

其实想想,redux 也可以算是一种单一输入源的方案。

在 Android/iOS app 的开发中,我们有时会把网络请求的结果缓存在本地数据库比如 SQLite 中,等下次打开时,先从本地数据库中加载缓存显示,然后再发网络请求,待网络请求返回后,先更新缓存,再将网络请求回来的数据直接显示到界面上。这时,界面上的数据就有两个输入源了,一个是缓存,一个是网络请求。更好的办法是,UI 永远只从缓存加载数据,而网络请求的结果只用来更新缓存。

单一读取源,多个写入源。


Comments