设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

使用React.js和应用缓存构建快速同步应用程序

2015-2-11 22:54| 发布者: joejoe0332| 查看: 2725| 评论: 0|原作者: LeoXu, 竟悟, 水母干, 船老大, 无若|来自: oschina

摘要: 对大部分应用系统来说,在某种程度上,应用程序的快速加载和及时取得最新数据两个方面同样重要。倾向于积极使用缓存数据,可能会导致提供的数据陈旧;而倾向于及时获取最新数据,可能会牺牲加载时间。当然,也可以鱼与 ...


它是如何运作的

  新的tab一打开,浏览器就会从应用缓存中获取我们的页面。我们的React应用或从Flux存储中获取数据 (1), 后者会去本地存储里抽取数据 (2). React 应用一安装,页面就会被加载 (3, 4). 然后,我们的页面会向应用服务器进行一次Ajax调用 (5), 发送应用的所有数据—其实就是一个带有所有Flux存储数据的对象. 服务器接收到用户所有的本地数据,并使用用户在数据库中的数据对其进行调和 (这会在下面的 "数据同步" 中有更详细的描述), 然后向应用返回最新的数据 (6). 应用会从服务器接收到最新的数据,并且更新每一个Flux存储 (7, 8).  Flux 存储一更新,存储会触发一个变化事件 (3), 而 React 组件就会更新他们的状态 (4). 当用户改变了什么东西的时候,就会发起一个动作 (11), 更新存储的数据 (7, 8); 当存储更新并触发变化时间的时候,我们会将数据持久化到本地存储中 (9) 而如果用户在线的话,就将数据持久化到数据库中 (10).

  如果你了解过有关 Flux 的东西, 下面这幅图看起来应该会很熟悉:

Our Flux data flow

(抱歉,这图看起来有点乱.)

  这幅数据流图的意思是,我们会向你快速的显示新的tab页,然后在一秒钟左右之内,我们将会用来自服务器的新数据更新你的页面. 这份新数据可能包含你在另外一个设备上对一个窗口小组件做出的变化 (像便条里的内容) , 或者也可能是一个慈善活动中“筹集资金" 实时统计.


  有关使用 Flux 构型最令人惊奇的一件事情就是通过调度器的远程数据流同步时间完全同用户的操作保持一致,使得调试异常的简单. 因为存储是我们应用状态的真实来源, 所以我们可以放心的让应用在存储的数据被远程的数据同步或者用户的输入改变时,仍然可以始终如一地响应. 我们仍然可以再整个应用中保持单项的数据流, 这使得代码理所当然的变得简单很多.


  在应用速度和数据实时性两者之间,我们已经找到了理想的平衡.


数据同步

  我提到过我们会在每次页面加载的时候向服务器同步你的数据。那我们是如何去实现这个东西的呢?


  我们通过为数据“块”的最后一次更新打上时间戳,然后在客户端(的Flux存储)上,以及远程的数据库中保存数据发生变化的时间戳(modified_at),这样的方式来处理同步. 例如,如果在你的一个便条窗口中进行了输入,就会把窗口的modified_at时间戳设置成现在,然后把你的便条内容保存到本地存储中,并入如果条件可能的话,也会保存到远程数据库中. 而后,下次你打开一个tab的时候,我们将会把有关窗口的数据发送到应用服务器,在那里会对跟该窗口相关的客户端时间戳跟数据库中保存的时间戳进行比对,并返回最新的数据.


为了简单起见,我们用Flux存储对象来进行数据的发送和接收. 这让我们可以无痛的用发送自应用的数据更新Flux存储, 因为我们明白它将会被保持最新,并且同我们的存储一样具有相同的数据结构.


  我们当前的同步过程肯定是不完美的: 在发生同步冲突的情况下,我们会简单地去获取最新的数据. 对我们而言,这只是一个可以接受的细节状况; 毕竟,我们可不是 Evernote. 即使这会变得不可接受,也可以在以后用更智能的数据合并和用户消息进行解决.


  让我们运行得更快一些 ! (或者说与呈现)


  加载应用缓存的页面很不错,但在我们向用户展示之前,我们仍然要运行React应用代码,并对所有的组件进行渲染. 对于一个相当大的应用而言,这可能要花上几百毫秒甚至超过1秒.


  为了能有一个快速的第一次加载体验, React 提供了一个方便的 renderToString 方法,让你可以先向浏览器发送DOM(让页面先出现) ,然后再连接上所有的侦听器 (让页面可交互). 这样就适应服务器端的预呈现了. 在我们的案例中,我们想是否可以用把它用在客户端上 — 而我们做到了.


  每次我们将数据持久化到本地存储时,我们也会将我们的React应用做成字符串,并将这个字符串保存到本地存储中. 然后,页面一加载,在我们做任何事情之前,我们会从本地存储中加载渲染好的应用并将它放到一个HTML元素中. 换言之,页面只用了3行JavaScript就加载了DOM! 对于我们的应用而言,预呈现减少了大概400毫秒的预加载时间。


  "见鬼":挑战和缺陷


  没有什么东西是完美的。重构的时候还是有些事情不那么有趣的。

再见吧, JQuery UI

  在转换到React过程中的一个速度损失让我们放弃了几乎所有的JQuery UI组件,比如 draggable. 这稍微烦人地让我们花了点时间来重新做之前已经做过的事情. 不过,事实证明我们还是可以依靠不断增长的实用的 开源React组件 来构建我们自身想要的东西.


"为什么, renderToString, 为什么?"

  另外一个小的实现上的挑战: 如果你用过React的 renderToString 方法, 你可能已经看到过这个错误:

React attempted to use reuse markup in a container but the checksum was invalid.

  当React在已经有预渲染DOM存在之后渲染它的应用时,它就要预计预渲染好的DOM应该同将要被渲染的DOM相同. 那就意味着你不能让像 Date.now() 和 Math.random() 这样的东西影响到你的DOM. 为了解决这个问题,你将可能要花点时间在你的差异编辑器上面,来比对这两个DOM字符串.


不够灵活的存储数据结构

  我们设计为应用同服务器返回的应用数据结构之间的不匹配敞开了大门. 在我们想生产环境推送新的代码之后,你第一次加载的页面视图会包含从应用缓存加载的老应用代码. 不过,从应用服务器返回的同步数据将会是结构化的,而我们的新版本会对其进行构造.

  所以,如果在新版本中我们决定对存储中的一块数据进行重命名或者移除,你的页面就会在新的tab第一次打开时被打断; 老的应用代码不会知道如何去处理它. 在打开下一个tab之前,你的浏览器可能已经获取到了最新的应用代码,并将其放到了应用缓存中,因此页面会运作得很好.

  为了防止新tab的打断, 我们需要为我们的存储数据维护一套可靠的内部API. 那样会有点儿痛苦.


说到代码的推送...

  如果我们搞砸了,弄坏了应用,每个看到一个破页面的用户都会在我们修复它之前看到一个额外的破页面. 应用程序缓存就会进行恼人的二次重新加载更新。


大家好,结局才会好

  切换到React和Flux是一件令人很愉快的事情. 我们的团队发现我们自己重新爱上了前端开发, 而我们做出的变化让新进工程师接触代码库容易了许多。

  在用户体验方面,我们的新tab一直在快速推进. 对于拥有优良网络条件的用户而言,这次的版本不会有太多的变化;但是对于其他人,他们是能在发现我们的应用不可用和喜欢上使用它之间发现不同的。


  因为Tab需要从横幅广告展示为慈善机构筹集基金的原因,更快的页面加载能增加在用户离开我们的页面之前看到的广告的数量. 这次的版本增加了大约12%的广告展示 (还有对应的筹资收入).


  当然,一个快速的应用并不会是一个好的应用; 它只是好的应用不会是一个马上就会让人讨厌的应用. 对于我们而言,它提供了未来更多有趣动人的事情的基础.

-----

  这是不是很有趣 ? 你想要通过一个有趣,充满活力的团队工作不 ? 我们在招人哦 ! 还有,如果你本周就在 San Francisco 附近, 我就会在周五的 React.js 推介见面会 上 — 如果你想要一次会谈的话就让我知道吧.

  感谢 Ti Zhao 和 Josiah Gaskin 对这篇文章的评论。


酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部