一、采用主动的服务器端缓存
我们一度认为应该增加首先扩展服务器端缓存结构,目前我们已经十分依赖Django的低级缓存(low-level cache),它有助于达到我们的目标,但是我们不得不在每次都要写语句来判断是否存在缓存到期或失效情况,我想这张摘自一场精彩演讲(an excellent presentation)的幻灯片能够反映出Django在缓存问题上面临的挑战:

此外,为了更好地从服务器端缓存中获益,我们的缓存系统看起来是一个多层次的结构:(先是)每个用户完整的页面缓存,然后是用户数据的模块化的缓存,(同时)每当数据变化时还要智能判断数据是否失效。因为在实施过程中已经遭遇到一些与缓存相关的(系统)错误(bugs),所以我们并不希望继续增加了缓存系统的复杂性。
更重要的是,还存在网络传递差异的问题,例如对一个新的TAB页面来说,在快速和慢速的因特网网络上的表现有着显著的差异,即使将我们的服务响应时间降低到小于1毫秒,对大部分用户而言,这个页面显示的还是不够快的。
不,这样可不行。
二: 在我们的页面上使用应用缓存
"应用缓存? 他不是个douchebag么?"
不,别这么粗鲁!
… 好吧, 也许他是有点儿. 在使用应用缓存之前,充分了解它的怪癖和陷阱是明智的.我们主要关心的是应用缓存会降低我们在调试时的透明度,因为我们服务器在轻便的请求上没有日志 (接下来我们将解决这个问题). 在代码变更后的另一个与之前不同的小问题是,在两个视图页上应用了这些变更: 它需要一个页面去提示浏览器获取资源, 另一个页面则去使用新的资源.这不是很理想, 但是在我们的案例中是可以接受的. 在一般情况下, 我们的团队在应用缓存的限制下相对没有多少烦恼; 更多我们的app不适用的情况下解决起来会更轻松.
好吧, 也许我们可以与应用缓存合作. 可能这是一个方法在不必通过大量的重构去实现它?
我们快速而粗糙的主意就是,使用 Django 处理视图模版并返回一个html页面来保持我们当前页面的原状.在任何用户数据变更时,浏览器会从服务器和应用缓存那里获取一个重新渲染的页面 .
我们的游戏计划:
我们将在当前页面上激活应用缓存, 所以它将会绕过服务器去加载.
当一个用户制造了一些数据改动而我们又想保留时, 我们的页面将会使用一个ajax请求去保存数据到数据库里,通常我们就是这么做的.
我们将会从应用缓存清单引入一个对用户特殊的版本号,所以对于每个用户来说这份清单都是独一无二的. 当用户更新任意数据时, 我们将会对这个用户的应用缓存清单的内容创建一个新的版本,而且浏览器会知道并获取页面资源来更新.
在客户端方面, 我们将会在用户修改任意数据时检查应用缓存并更新.浏览器将会获取用户的缓存清单, 查看已经被处理成一个新版本号的被更改的内容,并且重新获取页面的内容.
理论上, 当用户下次浏览这个页面时, 应用缓存会提供一个在服务端重新渲染过的最新的页面.
从好的方面讲,这些选择将会引入极小的工程投资.
一个小缺点:这个选项没起作用。
浏览器获取资源的速度不够快是主要的问题。 如果你在新的标签页修改了数据(例如,在你的便签里添加了一条笔记),然后在几秒内打开了一个新的标签,应用缓存可能还没有获取到你修改的新的页面,显示的依旧是你没添加笔记的旧页面。从用户体验的角度看,这就像是数据丢失 — 即使是技术上的数据延迟 ,也是我们无法接受的。
当多个设备参与时这个问题会变得更严重。如果你在设备A上对你的新标签页有修改,接着在设备B打开一个标签页,保证你得到的是旧的数据。在随后的页面加载之前你都看不到新的数据。
这不是很好。 抱歉,这是个快速而粗糙的选择。
三:面向模板的本地存储和应用程序存储
更简洁地做到这一点,我们可以结合客户端模板使用应用缓存,在本地存储数据。这看起来是个很好的选择,除了应用缓存的“第二页加载”那个问题所出现的糟糕情况,它是非常快的,并且它还可以清理掉我们的前端(重构...哇?)。作为奖励,我们的新标签页在在线的时候将被访问。
我们选择使用 React.js 作为模板是有一些原因的。最主要的一个就是我们有一些在其他领域使用应用的经验。我们也觉得学习曲线比Angular更浅显些,我们也是严肃地考虑过其他方案的。说来奇怪,长久以来建立一个前端框架都是在我们已有的jQuery上努力,我们的数据被改变更像是React中的“状态”,这会让我们转换到React更容易些。