解析 Twitter 前端架构 学习复杂场景数据设计

前几天刷Twitter,发现Nicolas(Engineering at @twitter. Technical Lead for Twitter Lite)发布了这么一条推文:javascript

twitter.jpeg

大致意思就是Twitter前端通过重构,已经彻底迁移到React+Redux+PWA技术栈了,后端也使用了nodeJS,实现了“前端一统天下”,lol。html

听到这个消息以后,我以为去深挖一下Twitter的Redux store组织架构,将会很是有意思。
这对于在复杂场景下的前端数据学习,以及React、Redux数据流设计十分有意义。前端

由于,在Redux数据流框架的思想下,对于数据的处理和分配彻底由前端掌握。
前端数据如何设计,设计的功力如何直接彻底决定整个项目的开发进度以及代码强健性,甚至还决定着页面的性能。java

本文将剖析Twitter前端数据结构层次,若是你对React技术栈不是很了解,也不妨碍阅读;一样,若是你对这套技术栈有兴趣的话,欢迎参看个人其余相似文章:node

欢迎关注个人主页,更多技术文章再也不错过。react

本文主体内容翻译自Ryan Johnson的文章:Dissecting Twitter’s Redux Store,笔者进行了必定程度的拓展。git

准备工做

想要看Redux store的前提是你须要配有React Developer Tools (RDT),在RDT tab中选中应用根节点。
确保选中以后,在console面板中输入:github

// $r is a shortcut that references the selected element in RDT
$r.store.getState();复制代码

接下来,咱们就能够看到Redux数据树,就像图中所示:redux

数据结构

设计分析

我建议你们花些时间对每一个不一样的state进行展开,并加以学习。但在这篇文章中,因为篇幅所限,我会挑选并深挖:后端

  • entities/tweets和
  • homeTimeline

两个最主要也是最核心的state进行剖析。这两个states包含了一条tweet的全部关联数据。

一条tweet,就像下图中我所发的:

推文举例

一条tweet内容的数据信息所有存储在entities/tweets/entities中,entities/tweets/entities能够理解为一个normalized的data table,它存储了全部tweets推文的信息;
在这个table中,每一条tweet都是一个键值对类型的js object:key为该条tweet的id,value为该条tweet的数据,也是一个js object。

下图中,我将第一条tweet展开,方便你们一探究竟:

推文设计.png

了解了tweet存储结构,咱们接下来看一下Twitter首页的timeline结构。
直观上,timeline必定包含了我的主页展现推文的信息。经过tweet id和刚才介绍过的entities/tweets/entities中的tweet相匹配,并最终加以在timeline上展现。
以下图:

timeline数据结构

每一个用户的首页timeline信息可homeTimelines/timeline找到。首页timeline展现的顺序,则按照timeline这个数组的顺序。也就是说,timeline数组index为0的条目,就是你在首页timeline上看到的第一条tweet;

重要的话再说一遍:
首页timeline上的每条tweet,都有一个惟一的id,这个id和上面介绍的,存储在entities/tweets/entities之中的tweet id相匹配。

看到这里,你也许会感叹:

“This is pretty much normalizing state shape 101 from Dan Abramov!”

没错,这样的范式也是Redux所推崇的,彻底的扁平化设计带来的开发体验和性能提高是无与伦比的。

固然,你可能会问为何Redux设计哲学,包括Twitter都在推崇扁平化的数据结构呢?
这个问题建议参考:Redux core concepts,这里讲的很是清晰,被收录在Redux core concepts中,强烈建议阅读。
若是您英语吃力,能够留言与我交流,就再也不展开了。

继续言归正传,咱们来讨论一下滚动时的异步请求设计。
首页timeline加载新tweets方式有两种:

  • 上拉加载 track tweets by top
  • 下滑加载 track tweets by bottom

第一种用于拉取更新的tweets,第二种用于拉取更旧的tweets;好比你新发了一条tweet,就要上拉,方可显示在timeline上;若是没有最新的,向下滑动到底部后,自动加载时间上更早的tweets。
用一个等式来表达:

top = new tweets,
and
bottom = older tweets

这种状况下,homeTimelines下的lastFetch.bottom和lastFetch.top,分别为时间戳,记录最后一次更新数据的信息(上拉和下滑)。

  • lastFetch.bottom: 记录最后一次向下滑动而更新数据的信息;
  • lastFetch.top: 记录最后一次下上拉取而更新数据的信息;

同时,
cursor.bottom和cursor.top值分别为一个tweet id,表示当前timeline上,最上边和最底部分别是哪一条tweet。

  • cursor.bottom: 记录屏幕最底部tweet ID;
  • cursor.top: 记录屏幕最顶部tweet ID;

同时, homeTimelines里面还记录了isLoadingDirections.bottom和isLoadingDirections.top来表示数据加载的触发源头。

如图:

记录信息.png

最后一个很是有意思的是,entities下除了存在entities/tweets以外,还分别有cards, lists and users;

  • entities/tweets
  • entities/cards
  • entities/lists
  • entities/users

来表示不一样的推文特性。

当你打开这其他三项的时候,会发现这三项与entities/tweets保持在相同的结构,他们都有一个fetchStatus的data table,key为tweet id, value为加载状态,据统计一共有一下几种:

  • ‘none’;
  • ‘loading’;
  • ‘loaded’;
  • ‘failed’.

状态截图

这几种状态的设置无外乎这么几个目的:

  • 保证在loading状态或loaded的tweet不会再发送请求给server;
  • 在未加载完时,能够显示加载动画或者展位图;
  • 在加载失败时,能够显示失败提示或者在此请求时进行补救。

总结

本文分析了Twitter在采用Redux架构下的数据设计结构,在一个复杂的场景下,但愿引发读者对redux能有一个更深刻的认识。

本文主体内容翻译自Ryan Johnson的文章:Dissecting Twitter’s Redux Store,笔者进行了必定程度的拓展。

Happy coding!

PS: 做者Github仓库,欢迎经过代码各类形式交流。

相关文章
相关标签/搜索