Top Reasons Why Your Angular App Is Slow

栏目: IT技术 · 发布时间: 4年前

内容简介:Is your app slow? Learn what to watch out when debugging poor performance in your Angular apps!Angular is, by default, a fast and performant framework. While it leaves ample space for improvement by opting out some of its magic, we almost never have to do

Is your app slow? Learn what to watch out when debugging poor performance in your Angular apps!

Top Reasons Why Your Angular App Is Slow

Photo by Chris Liverani on Unsplash

Introduction

Angular is, by default, a fast and performant framework. While it leaves ample space for improvement by opting out some of its magic, we almost never have to do anything special to write extremely performant code for the average app.

With that said, some difficulties will eventually arise when we are writing either performance-critical apps, apps with large and complex content , or apps updated extremely frequently .

There are loads of resources and popular advice out there about improving performance with Angular apps. While most of it is totally good and valid advice, what I have personally found while mitigating performance issues is not often talked about.

In this article, I want to show some of the most important reasons why Angular apps become slow at scale. What we will see is that it doesn’t really matter the framework used — these tips will be valid for any framework.

Tip:as we all know, rewriting code is a recipe for bad code. Use tools like Bit ( Github ) to “harvest” reusable components from your codebase and share them on bit.dev . This way you and your team can easily find them, import them to any project and develop them further if needed. It’s a good way to guarantee high quality, scalable and maintainable code.

Top Reasons Why Your Angular App Is Slow
Example: searching for shared components in bit.dev

Micro Optimizations: Do they Matter?

In my experience, this is a very misunderstood topic among developers. Whenever I’ve had to deal with performance issues, many colleagues were pointing out at code that could have been causing these issues, which very often happened to be micro-optimizations that would only save a few milliseconds.

Some examples:

  • “We’re using too much reduce, map and filter, let’s replace them all with for loops!”
  • “Let’s use a dictionary for accessing data faster!”
  • “Bitwise Operators!”

I always thought there was something else going on.

The ones above are all very valid pointsif you are coding performance-critical applications, frameworks or libraries. The first thing people look at when trying to find the reason why they’re experiencing poor performance is to debug every function call:

  • “How much did it take to find an item in this list [of maybe 300 items]?
  • “How long did it take to sort [800 items]”?

But when you’re developing an application, these optimizations may count much less than you think.

This is not to say it can’t happen, but I would start questioning two other possible issues first:

  • How much is the app actually rendering?
  • How often is the framework re-rendering components?

As we will see, even when following best practices, sometimes they’re not enough to prevent slow performance. And most often it’s not due to the framework we’re using but to our code or architecture.

Your app is rendering too often

Let’s start with this quite common issue: your application re-renders components unnecessarily, making your application slower than it could be. This is both easy to solve and easy to cause.

Change Detection

Setting the default change detection to OnPush is an almost mandatory step if your application is suffering from slow performance, or if you want to prevent from it happening in the future.

By setting your components to update only “on push”, you prevent re-rendering components that don’t need to be checked. It’s straightforward and its usage is greatly simplified when using Observables and the Async pipe.

Async Pipe

Even if you’re using OnPush and the async pipe in your templates, you may still be re-rendering more than it is actually needed.

For example, in my experience, preventing observables from emitting is a good way to prevent your component from re-rendering. For example, you may use operators such as filter and distinctUntilChanged to skip re-renderings altogether.

Another issue that I’ve experienced even when using Observables and Async pipe was due to selecting items from a store without selectors. If we write and use granular selectors, we only receive updates from the state slice affected .

If we select the whole object from a Redux state tree, the selectors will emit every time the tree-changed, and as a result, we will end up triggering updates on components that are virtually unaffected .

This is a seemingly subtle improvement that ended up making one of my applications from barely usable to decently performant on IE11 .

For more information about improving efficiently with RxJS, check out my article below:

High-Frequency Updates

This is a use-case where Angular doesn’t excel at, and it is probably due to Zone.js, which is also the reason behind Angular’s magical change detection.

Zone.js will monkey-patch all events and will schedule a change detection when any of these happened. That means that if your application is streaming events at a fast rate (Websocket, or even DOM events), for each event received, Zone will trigger a change detection. There’s definitely room for improvements in similar cases.

I have talked in depth about this at the link below:

Of course, you don’t need to remove Zone from your app to solve this issue. Here are a few steps you can take instead:

ngZone.runOutsideAngular(callback)

Your app is rendering too much

No matter how fast your framework is, if you are rendering thousands of complex components in one go, the browser is eventually going to show some amount of lag.

Even if maybe on your Macbook Pro it isn’t very noticeable, slower machines will definitely struggle, and you should think that not everyone is using a powerful machine.

It’s extremely important to make sure that components rendered many items (eg. within lists) are optimized in a particular way.

How can you solve this?

Keying

This is the simplest and probably most well-known technique which is baked in most libraries. The concept is simple: we assign a key to each item of a list, and the library will re-render it only if the key has changed.

This works great when adding/removing items or when the amount of items changes is limited but still doesn’t solve a performance issue if we render a vast amount of items at once. For example — if we render a very large list at page load.

Virtual Scrolling

Only render what the user can see.

While this has accessibility/usability implications to be aware of, it is one of the best methods to improve perceived performance and avoid getting the page frozen for an unreasonable amount of time, which is less than you’d think .

It’s pretty easy to implement: the Angular CDK provides a utility for this!

Async Rendering

This is an older technique, to which I’d prefer virtual scrolling, but that can still be better than rendering 1000 items at once and it is very easy to implement without having to write much code.

The concept is this: start rendering a limited number of items (ex, 50 out of 500), then schedule a subsequent rendering with the next 50 items using setTimeout(0) until all items are rendered . This is a simple technique, so the results are also simple — but the browser won’t get stuck for 250ms while rendering.

Lazy Rendering

Not everything has to be rendered right away, sometimes we can simply render a component when the user needs to interact with it.

Here’s a similar use-case I worked on: I was working on a page that used many instances of Quill , a famous WYSIWYG library.

This is a great tool, but it’s pretty heavy. Instantiating one of its components took 20–30ms, and I had to render hundreds of them on a page. My Macbook pro crashed.

Instantiating it right away was pretty silly: WYSIWYG can be simple HTML when not interacted with. I could simply instantiate the component when the user needed to, e.g. when hovered or clicked on. All performance issues were gone!

Lazy Listeners

This is directly related to the previous points: subscribing and listening to too many events may be quite expensive.

Avoiding subscribing to too many events can be done in different ways:

  • If you have a large list of items with DOM handlers, make sure you only subscribe to the items that are visible (virtual scrolling help with this)
  • Sometimes you may want to only create one single global event from within a Service, rather than subscribing to the event in each Directive/Component

Some code… is just slow

If you’ve done your research and figured out that your app doesn’t render that much and doesn’t render that often, then your code may just simply be quite slow. This is probably due to some heavy scripting and not DOM-related.

Cheer up! It’s a good thing, as nowadays we have the tools to resolve this sort of issue.

  • Use WebWorkers . The Angular CLI also provides a command to generate a WebWorker in a snap. When is this a suitable route? Simple — when your code has nothing to do with rendering and takes a while to execute. This is normally crunching numbers, data-processing, etc. That’s great for Redux, right? Wait, don’t do that yet.
  • Use WebAssembly , for example using AssemblyScript . Read this case study from Figma for more information.

If the above are not routes you’re comfortable with, or simply don’t solve your use case, then it’s time to try micro-optimizations and see by how much they can improve your runtime performance:

Takeaways

  • Opt-out of the framework’s magic: make sure you use ChangeDetection.OnPush and TrackBy for arrays
  • Render less often by surgically triggering change detections on your components. Run outside Zone when needed.
  • Try to render less using a variety of techniques such as virtual scrolling and lazy rendering
  • Don’t listen to everything: subscribe to only the items that are visible, and subscribe to only one global event listener

Resources

If you need any clarifications, or if you think something is unclear or wrong, do please leave a comment!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

重来2

重来2

[美] 贾森·弗里德、[美] 戴维·海涅迈尔·汉森 / 苏西 / 中信出版社 / 2014-4-8 / 39.00元

“不再需要办公室”,这不仅仅是未来才有的事——它已经发生了。现在,轮到你迈开脚步,跟上时代的步伐了。 上百万的员工和成千上万的企业已经发现了远程工作的乐趣和好处。然而,远程工作方式还没有成为常见的选择。事实上,远程工作的技术手段都已齐备。还没有升级换代的,是人们的思想。 这本书的目的就是帮你把想法升级换代。作者会向你展示远程工作的诸多好处:可以找到最优秀的人才,从摧残灵魂的通勤路上解脱......一起来看看 《重来2》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具