在Scalyr,我们最近着手重写我们的Web客户端。ScalyrLogs是一种多用途监控和日志分析工具,可在我们的专用日志数据库中执行大多数查询。几十毫秒,但是每次页面响应都需要加载页面,可能需要几秒才能呈现给用户。单页设计架构保证不会拖累后??台的强大性能,于是我们开始寻找合适的框架,AngularJS脱颖而出。遵循“failfast”的原则,我们开始了挑战之旅:日志视图Write的重新设计。测试应用程序框架确实是一项严峻的挑战。当用户点击日志中的任意一个词时,我们需要搜索相关信息,而页面上可以点击的元素数不胜数;我们希望日志的分页功能能够得到即时的反馈。其实我们已经提前拿到了下一页的日志数据,所以用户界面的更新就成了瓶颈。如果使用AngularJS直接实现日志视图的换页功能,需要1.2秒,但如果细心优化,可以降低到35毫秒。这些优化被证明也适用于应用程序的其他部分,并且也很好地适应了AngularJS。但是我们必须打破一些规则来实现我们的想法,稍后讨论。一个Github更新的日志演示AnAngularJS日志查看器本质上,日志视图是一个日志消息列表,每个单词都是可点击的。所以给DOM元素添加Angular指令,简单实现如下:}单页应用有上千个token是很正常的,在早期测试中我们发现下一页进入log需要几秒才能执行JavaScript。更糟糕的是,不相关的操作(比如点击导航下拉框)没有延迟。AngularJS的高手说过,数据元素绑定的个数最好控制在200以下,对于我们来说,一个词就是一个元素的地方,这个数量已经远远超过了这个数量。使用Chrome的JavaScriptprofiler工具进行分析,我们可以快速定位到两个拖延点。首先,每次更新都需要花费大量时间来创建和销毁DOM元素。如果新视图的行数不同,或者任何一行的单词数不同,Angular的ng-repeat指令将创建或销毁DOM元素。价格太高了。其次,每个单词都有自己的changewatcher,AngularJS会监视这些单词,一旦鼠标点击就会触发,这是影响无关操作延迟的罪魁祸首(下拉菜单导航)。优化#1:缓存DOMelements我们创建了ng-repeat指令的变体。在我们的版本中,如果绑定数据的数量减少,多余的DOM元素将被隐藏而不是销毁。如果过段时间元素数量减少了又增加了,我们就会重用这些缓存的元素。优化#2:Aggregatewatchers花在调用changewatchers上的所有时间大部分都被浪费了。在我们的应用程序中,特定词的数据绑定永远不会改变,除非整个日志消息发生变化。为此,我们创建了一个指令“hides”,它隐藏了子元素的changewatchers,并且仅在特定的父元素表达式发生变化时调用它们。通过这种方式,我们避免了每次鼠标点击或其他小修改时的全面变化观察者(为了实现这个想法,我们稍微修改了AngularJS抽象层,我们将在后面详细说明)。优化#3:推迟元素创建如上所述,我们为日志中的每个单词构建了一个DOM,我们可以为每一行使用单个DOM元素来获得相同的视觉呈现;其他元素是响应鼠标点击操作的,因此,我们决定推迟这部分的创建,只在鼠标移动到某一行时才创建。为此,我们为每一行创建了两个版本,一个是简单的文本元素,用于显示完整的日志消息,另一行是占位符,用于显示填写每个单词的最终效果。这个占位符一开始是隐藏的,只有当鼠标移到那一行时才会显示,此时简单的文本行会被隐藏。正如我们将在下面看到的,显示占位符是填充单词元素的方式。优化#4:避免监控隐藏元素我们创建了另一个指令来防止监控隐藏元素,该指令支持优化#1,我们有比原始数据更多的隐藏DOM节点,因此必须消除对额外DOM节点的监控。这也支持优化#3,从而更容易推迟创建单词节点。因为在这行数据的标记化版本出现之前,我们不会创建它。以下代码是所有优化后的样子,我们的自定义指令以粗体显示。
