Optimization & Performance
Deferrable Views
Can be used in component template to defer the loading of select dependencies within that template.
These dependencies may include components, directives, pipes, and associated CSS.
They are powerful tool used to refuce the initial bundle size or defer heavy components that may not ever be loaded until a later time.
To use this, you can wrap a section of your template in a
@defer
block which specifies loading conditions.Deferrable views support a series of
triggers
,prefetching
and several sub blocks used forplaceholder
,loading
anderror
state management.
It is highly recommended that any defer loaded component that might result in layout shift once the dependecies have loaded be below the fold or otherwise not yet visible to the user.
In order for dependencies to be deferrable they must:
Be
standalone
. Non-standalone dependencies cannot be deffered and will still be eagerly loaded, even if inside@defer
blocks.They must not be directly referenced from the same file, outside of
@defer
blocks; this includesViewChild
queries.
Behavior with Server-side Rendering:
@defer
blocks always render their@placeholder
(or nothing if not specified).Triggers are ignored on the server.
@defer
block
@defer
blockThe content of the main
@defer
block is the section of content that is lazily loaded.All of the content will appear once the specified
trigger
orwhen
condition is met and the dependencies have been fetched.By default, a
@defer
block is triggered when the browser state becomesidle
.
Nested @defer
blocks (avoiding cascading loads)
There are cases where nesting multiple
@defer
blocks may cause cascading requests. An example of this would be when a@defer
block with an immediate trigger has a nested@defer
block with another immediate trigger. When you have nested@defer
blocks, make sure that an inner one has a different set of conditions, so that they don't trigger at the same time, causing cascading requests.
@placeholder
block
@placeholder
blockIs an optional block that declares content to show before the defer block is triggered.
This placeholder content is replaced with the main content once the loading is complete.
You can use any content in the placeholder including, plain HTML, components, directives, pipes.
Just keep in mind that dependencies of the placeholder are eagerly loaded.
minimum
parameter (optional)
Is specifed in time increments of miliseconds (ms) or seconds (s).
It exists to prevent fast flickering of placeholder content in the case that the deferrable dependecies are fetched quickly.
@defer {
<large-component />
} @placeholder (minimum 500ms) {
<p>Placeholder content</p>
}
@loading
block
@loading
blockIs an optional block that allows you to declare content that will be shown during the loading of any deferred dependencies.
Its dependencies ae also eagerly loaded (similar to
@placeholder
).
@defer {
<large-component />
} @loading (after 100ms; minimum 1s) {
<img alt="loading..." src="loading.gif" />
}
minimum
parameter (optional)
Is specifed in time increments of miliseconds (ms) or seconds (s).
To specify the minimum amount of time that this placeholder should be shown.
after
parameter (optional)
Is specifed in time increments of miliseconds (ms) or seconds (s).
The amount of time to wait after the loading begins before showing the loading template.
@error
block
@error
blockIs an optional block that allows you to declare content that will be shown if deferred loading fails.
Its dependencies are also eagerly loaded.
@defer {
<calendar-cmp />
} @error {
<p>Failed to load the calendar</p>
}
Triggers
There are two options for configuring when a swap is triggered:
on
andwhen
.
on
on
Specifies a trigger condition using a trigger from the list of available triggers bellow.
Multiple triggers can be defined at once. For instance:
on interaction; on timer(5s)
means that the defer block will be triggered if the user interacts with the placeholder, or after 5 seconds.Multiple
on
triggers are alwaysOR
conditions. Similarly,on
mixed withwhen
conditions are alsoOR
conditions.
on iddle
on iddle
Will trigger once the browser has reached an idle state (detected using
requestIdleCallback
API under the hood).Is the default behavior.
on viewport
on viewport
Will trigger when the specified content enters the viewport.
By default the
@placeholder
will act as the element watched for entering the viewport as long as it is a single root element node.
@defer (on viewport) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
}
Alternatively , you can specify a
template reference variable
in the same template as@defer
as the element that is watched to enver the viewport.
<div #greeting>Hello!</div>
@defer (on viewport(greeting)) {
<greetings-cmp />
}
on interaction
on interaction
Will trigger when the user interacts with the specified element through
click
orkeydown
events.By default, the placeholder will act as the interaction element as long as it is a single root element node.
@defer (on interaction) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
}
Alternatively , you can specify a
template reference variable
as the element that triggers interaction.
<button type="button" #greeting>Hello!</button>
@defer (on interaction(greeting)) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
}
on hover
on hover
hover
triggers deferred loading when the mouse has hovered over the trigger area.Events used for this are
mouseenter
andfocusin
.
By default, the placeholder will act as the hover element as long as it is a single root element node.
@defer (on hover) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
}
Alternatively , you can specify a
template reference variable
as the hover element.
<div #greeting>Hello!</div>
@defer (on hover(greeting)) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
}
on immediate
on immediate
Triggers the deferred load immediatly, meaning once the client has finished rendering, the defer chunk would then start fetching right away.
@defer (on immediate) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
}
on timer
on timer
Would trigger after a specified duration
timer(x)
.The duration can be specified in
ms
ors
.
@defer (on timer(500ms)) {
<calendar-cmp />
}
when
when
Specifies a condition as an expression that returns a boolean.
When the expression becomes truthy, the placeholder is swapped with the content.
If the
when
condition switches back tofalse
, the defer block is NOT reverted back to the placeholder.
Prefetching
Defer allows to specify conditions when prefetching of the dependencies should be triggered.
when
andon
are associated with defer controls when to render.prefetch when
andprefetch on
controls when to fetch the resources.This allows for more advanced behaviors, such as letting you start to prefetch resources before a user has actually seen or interacted with a defer block, making the resources available faster.
In the example below, the prefetching starts when a browser becomes idle and the contents of the block is rendered on interaction.
@defer (on interaction; prefetch on idle) {
<calendar-cmp />
} @placeholder {
<img src="placeholder.png" />
}
Testing
Image Optimization NgOptimizedImage
NgOptimizedImage
The
NgOptimizedImage
directive makes it easy to adopt performance best practices for loading images.Do not use background images in CSS, convert them to
absolute
components which can useNgOptimizedImage
.
Getting Started
Enable the Directive.
<img ngSrc="cat.jpg">
, replace thesrc
attribute withngSrc
.
Mark images as
priority
.<img ngSrc="cat.jpg" priority>
This will apply optimizations like:
fetchpriority=high
loading=eager
Automatically generate a
preload link element
if rendering on the server.
By default non-priority images will have
loading=lazy
.
Include
width
andheight
.For responsive images, the values should be the intrinsic size of the image file. It is also important to set
sizes
.For fixed sized images, the values should reflect the desired rendered size of the image.
The aspect ratio should always match the intrinsic aspect ratio of the image.
Using fill
mode (background image behavior)
fill
mode (background image behavior)Use it for cases you want to have an image fill a containing element.
When using
fill
attribute, you must not includewidth
andheight
attributes.For the fill image to render properly, its parent element must have
position: relative
,position: fixed
orposition: absolute
.
<img ngSrc="cat.jpg" fill />
Placeholder placeholder
placeholder
NgOptimizedImage
can display an automatic low-resolution placeholder for your image if you're using a CDN or image host that provides automatic image resizing.Adding this attribute automatically requests a second, smaller version of the image using your specified image loader.
You can also specify a
base64
without an image loader withplaceholder="data:image/[imagetype];[data]"
.Keep in mind that large data URLs increase the size of bundles and slow down page load.
The
placeholder
will also apply ablur
effect to this small image.To render without this effect you may provide a second attribute
[placeholderConfig]="{blur: false}"
.
<img ngSrc="cat.jpg" width="400" height="200" placeholder />
Configuring a Image Loader
Runtime Performance Optimization
Change detection is highly optimized and performant, but it can still cause slowdowns if the application runs it too frequently.
How to control and optimize the change detection mechanism by skipping parts of your application and running change detection only when necessary.
How to Profile an Application - with Angular DevTools, Chrome Web Extension
Angular DevTools, Chrome Web Extension
Go to the
Profiler
tab and startRecording
.As Angular performs change detection in the application, you'll see bars corresponding to the individual change detection cycles.
Each bar shows how much time Angular spent on a detection cycle and what Component was running.
Performance Pattern
What problem it is.
How to identify it.
How to resolve it.
Zone Pollution
This happens when Angular zone wraps callbacks that trigger redundant change detection cycles.
Polluting the zone happens when we run an initialization logic that uses
requestAnimationFrame
,setTimout
oraddEventListener
.You can identify this problem, by looking for unexpected change detection cycles in the Profile output.
The solution is to move initializations outside the Angular zone.
For example, you may initialize a chart, and see that interations with the chart bars, are triggering change detection cycles that are useless.
export class Component implements OnInit {
constructor(private zone: NgZone) {}
ngOnInit() {
// Now interations with the chart will not trigger change detection
this.zone.runOutsideAngular(() => SomePlotChart("chart", data));
}
}
Out of Bounds
This happens when local state changes triggers out of bounds change detection.
Meaning that changes in one place may trigger detection changes of other unrelated places.
You can identify this problem, by change detection performed in components outside the scope of the change.
The solution is to use
OnPush
change dectection and consider refactor.
Recalculation (of referentially transparent expressions)
This happens when Angular recalculates the same template expression repeatedly, even though they depend only on parementers that values does not change. (Redundant calculations)
You can identify this problem, when detection for changes takes longer than expected given the state changes.
The solution involves either pipes or memoization of these values.
Large component trees
You may observe framedrops if you have complex component trees with a lot of instances.
The solution involves making the tree smaller, with techniques like on-demand rendering, with like virtualization or pagination.
<cdk-virtual-scroll-viewport>
Last updated