Angular JS 缩放和性能

Posted

技术标签:

【中文标题】Angular JS 缩放和性能【英文标题】:Angular JS Scaling & Performance 【发布时间】:2013-07-13 10:40:33 【问题描述】:

我们正在为我们为银行构建的 Angular 应用程序的性能问题而努力。

很遗憾,显示代码的sn-ps是违约行为。无论如何,我可以描述一些正在发生的主要问题,我希望可以推荐最佳做法。

应用结构:

本质上是一个巨大的多表单页面。 每个表单都有自己的部分,嵌套控制器和部分大约 3 层深。 相同的表单在 json 对象集合上重复。 每个表单都绑定到它所重复的对象/模型。 我们应该支持页面上 1-200 个表单。

如果你看一下时间线。我们在 jQuery parse html 方法、jQuery recalculate stye 方法、GC Event (Garbage Collection) 上花费了大量时间。我想最小化这些应该会加快速度。它们都是 Angular 生命周期的一部分,但可能有更好的方法来避免它们。以下是分析器的一些屏幕截图:

最终,由于重复表单的数量超过 5 个,应用程序变得迟缓。每个表单与其他表单相对无关。我们已尝试不查看表单之间的任何共享属性。

【问题讨论】:

问题是……? 您是否尝试过将此函数调用追溯到您自己的代码库(或调用它的角度?)@Stewie 我相信问题(尽管有些编纂)是一般性能瓶- 可能出现在 AngularJS 应用程序中的脖子?应该如何进行性能/优化?我相信 chrome 的 Batarang 插件有一些性能方面的东西(刚刚检查过,虽然我没有使用过,但确实有一个性能选项卡,它可能会有所帮助)。 “AngularJS 应用程序中可能出现的一般性能瓶颈是什么?”与任何 javascript 应用程序相同的瓶颈,您创建的对象越多,GC 的时间就越长。 HTML5rocks 网站上有很好的资源。 @mpm 我想你也会遇到问题,尽管通过使用 $emit/$broadcast 未经检查的方式创建冒泡的事件。我的意思是是的,您所说的通常仍然是许多正在创建的对象/被调用的函数的问题,同样的一般问题,但是如果没有程序员控制的事件总线,您通常不会在 Angular 之外的 JS 中遇到这种情况。 你能模拟一个 jsfiddle 或 plnkr 吗?我知道你不能分享你的客户代码,但模仿结构应该不会太难,这样我们就可以更好地了解你是如何实现的。 【参考方案1】:

一般来说,如果有超过 2000 个数据绑定处于活动状态,AngularJS 的性能就会很差,即每个 $digest-cycle 都会对范围内的 2000 个项目进行脏检查。因此,Ng-repeat 对性能有很大影响;每个重复的项目至少设置两个绑定,不计算项目内部使用的任何其他数据或指令。

AngularJS 背后的一位开发人员对脏检查的细节及其在这个 SO 答案中的性能进行了出色的描述:

https://***.com/a/9693933/179024

该答案下方的评论线程值得一读,我还在同一页面下方的答案中分享了一些关于它的想法:

https://***.com/a/18381836/179024

【讨论】:

我认为 Misko 的回答虽然信息丰富,但仅部分解决了这个问题。这里问题的很大一部分似乎是 Angular 是单独创建每个元素,而不是将它们批处理到同一个片段中。 问题可能很简单,就是有很多重复的表单,在 istelf 中每个表单都有很多重复的项目,每次 $digest 运行时被观看的项目数量会导致网站速度变慢.检查这一点的一个好方法是 Chrome 的batarang 插件,它将实时显示摘要时间。但由于 OP 无法显示任何代码,因此很难确定问题所在。 OP 确实显示了事件时间线,并且显示的大部分活动都是一长串 HTML 解析。 Angular 这样做而不是在一个片段中创建它们可能有一个很好的理由,但我认为这就是为什么它在这里很慢;脏检查似乎不是一个大的贡献者。 我怀疑上面的 html 解析是在摘要周期内发生的,所以这可能与 $digest 性能无关。 @jssebastian - 不,这确实可能与 $digest 时间无关。如前所述,如果 OP 想要调查它,我会推荐 Batarang-plugin。【参考方案2】:

如果没有关于您的问题的更多信息,很难提供解决方案,但我最近遇到(并解决了)a performance issue 可能与您看到的类似,并且与 $digest 循环无关。

您会发现大多数关于 angularjs 性能的讨论(包括excellent post from Misko)都是关于脏检查和 $digest 循环的性能。但这不是您使用 angularjs 会遇到的唯一性能问题。第一步应该是确定消化周期是否是您的问题。为此,您可以使用batarang,或者只查看您的应用程序,看看它何时运行缓慢。当摘要周期很慢时,基本上任何与 UI 的交互都会很慢。

OTOH,您可以拥有一个具有快速消化周期的应用程序,即只有在加载、切换视图或以其他方式更改要显示的组件集时才会很慢,这可以在分析中体现为花费大量时间进行解析HTML 和垃圾收集。就我而言,这是通过对要显示的 html 模板进行一些预计算来解决的,而不是到处依赖 ng-repeat、ng-switch、ng-if。

我使用了一个 ng-repeat="widget in widgets",其中包含一个关于小部件类型的 ng-switch,以显示任意一组小部件(自定义自包含指令)。将其替换为代码以生成特定小部件集的角度模板,可以加快从大约 10 秒到几乎即时的路由切换。

您可以查看上面的 google 群组主题,了解有关我如何解决我的特定问题的更多信息,或者如果您需要一些具体建议,请提供有关您的应用程序的更多信息。

【讨论】:

您还在用这种方法获得 2 路数据绑定吗? @matheus:是的,每个小部件的内容仍然有两种方式绑定。另一方面,要显示的小部件列表没有,它是通过连接每个单独的小部件的模板生成的。【参考方案3】:

您需要创建自定义指令以遏制 angular.js 的性能问题。与 ember angular 不同,所有的花里胡哨都打开了,你可以把它调低。以下是我为帮助您而创建的一些指令。并非您的应用程序中的所有数据都需要双向数据绑定,因此您可以通过在需要的页面中放弃监视表达式来节省宝贵的 CPU 功率。所有这些指令都一次绑定数据,然后不理会它。

https://gist.github.com/btm1/6802599

https://gist.github.com/btm1/6802312

https://gist.github.com/btm1/6746150

上面的答案之一是关于 ng-repeat 具有巨大的性能影响,所以我给你“set-repeat”一个一次性数据绑定重复指令:)

【讨论】:

【参考方案4】:

很抱歉将其作为“答案”,因为我还没有足够的分数来发表评论。

我们的 AngularJS 应用程序也遇到了类似的问题。使用 'batarang' 似乎必须处理大量范围对象,并且它们相关的 $watch 表达式会产生性能问题。这让我们想知道是否应该使用另一个框架或类似 ReactJS 的东西来处理“视图”部分。

【讨论】:

【参考方案5】:

将 DOM 操作转移到自定义指令和大量 $watch 的 $watch 问题之间的中间立场是使用“bind-once”语义。

这对于一旦数据可用就不可改变的数据非常有用。见bindonce

【讨论】:

【参考方案6】:

尽量避免以下情况

    如果你有超过 50 个元素,请避免使用 ng-repeat 一次列出清单,避免手动观看

    不要盲目地使用 ng-click、ng-mouseenter、ng-mouseleave 等鼠标事件,直到迫切需要,尝试通过使用 $event 对象以及 js 的事件传播概念来减少它们的数量

    尽可能使用 scope.$digest 而不是 scope.$watch,这样可以确保摘要循环仅在子作用域上执行

      尝试在一个父控制器中使用嵌套范围,即一个或两个控制器,并将可重用逻辑保留在父控制器中,我在使用 Ui-router 时在嵌套状态下使用它(以满足需要更改 URL 而不刷新页面的请求) .

    最重要!从 HTML 中删除所有过滤器!

以上所有内容都会在您的应用程序的所有范围内触发一个摘要循环,因此即使视图已被渲染成角度,也很有可能再次执行无情的摘要循环

【讨论】:

你会用什么来代替 ng-click 和 ng-repeat? 使用事件捕获和冒泡的 javascript 概念来减少 ng-click 和 ng-repeat 使用单个绑定属性(较新版本的 angular js) 除非您遇到性能问题或知道您会遇到问题,否则我不会违背常规。前任。如果你不使用事件的内置指令,如果你想更新你的模型,你就会被触发 .$digest() (从当前范围向下触发脏检查)或更糟糕的 $apply() (它从 $rootScope 触发),这比使用 ng-click 或其他(只是为该范围注册另一个观察者)效率低。 从 HTML 中删除所有过滤器违背了使用过滤器的目的,您还不如使用服务。如果您在过滤器中保留简单的操作(它们的用途),您将不会招致任何明显的性能损失。【参考方案7】:

这只是一个链接!这只是我在阅读本文时的一个想法,我还没有探索过这个,但可能有人这样做了,所以我正在等待他们对我的想法的回复。如何使用共享网络工作者从 ui 线程中获得大量繁重的处理? https://github.com/h2non/sharedworkers-angular-poc

我的另一个想法是一个更简单的想法。您的应用会从无限滚动中受益吗?我的意思是这些表格可能并不都适合屏幕,它们没有相互连接,所以为什么不根据需要绘制它们呢?将它们加载到内存中,然后相应地绘制它们。

【讨论】:

【参考方案8】:

就像在任何其他性能优化中一样,了解如何分析应用程序以找到真正的瓶颈非常重要。然后你就可以一一解决了。我通常按​​以下顺序对抗瓶颈:

我的 javascript 代码 在每个空闲摘要周期运行的角度表达式(复杂的观察者和过滤器) 角度构造(ng-repeat,复制摘要循环的对象)

我已经逐步分析了一个角度示例,展示了如何在每个步骤中识别瓶颈。 http://bahmutov.calepin.co/improving-angular-web-app-performance-example.html

【讨论】:

【参考方案9】:

为了提高生产性能,请阅读以下非常好的单行:

引用 AngularJS 文档:

默认情况下,AngularJS 将有关绑定和范围的信息附加到 DOM 节点,并将 CSS 类添加到数据绑定元素:

由于 ngBind、ngBindHtml 或 ... 插值,绑定数据和 CSS 类 ng-binding 被附加到相应的元素。

当编译器创建了一个新的作用域时,作用域和 ng-scope 或 ng-isolated-scope CSS 类都附加到相应的元素上。然后可以通过 element.scope() 和 element.isolateScope() 访问这些范围引用。

Protractor 和 Batarang 等工具需要此信息才能运行,但您可以在生产环境中禁用此信息以显着提升性能:

myApp.config(['$compileProvider', function ($compileProvider) 
  $compileProvider.debugInfoEnabled(false);
]);

您可以阅读更多详情here

【讨论】:

以上是关于Angular JS 缩放和性能的主要内容,如果未能解决你的问题,请参考以下文章

Angular Js Datatable:在响应式插件中,ng-click 在缩放(折叠)模式下不起作用

Angular.Js 性能、大型数据集、ng-repeat、带有过滤器和双向绑定的 html 表

JS框架性能对比:Soild 高居榜首,VueReact 和 Angular 竟纷纷跌出前十

angular4.0和angularJSreact.jsvue.js的简单比较

Angular.js浅谈

Immutable.js 与 Angular 2