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
@deferblock which specifies loading conditions.Deferrable views support a series of
triggers,prefetchingand several sub blocks used forplaceholder,loadinganderrorstate 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@deferblocks.They must not be directly referenced from the same file, outside of
@deferblocks; this includesViewChildqueries.
Behavior with Server-side Rendering:
@deferblocks always render their@placeholder(or nothing if not specified).Triggers are ignored on the server.
@defer block
@defer blockThe content of the main
@deferblock is the section of content that is lazily loaded.All of the content will appear once the specified
triggerorwhencondition is met and the dependencies have been fetched.By default, a
@deferblock is triggered when the browser state becomesidle.
Nested @defer blocks (avoiding cascading loads)
There are cases where nesting multiple
@deferblocks may cause cascading requests. An example of this would be when a@deferblock with an immediate trigger has a nested@deferblock with another immediate trigger. When you have nested@deferblocks, 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:
onandwhen.
on
onSpecifies 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
ontriggers are alwaysORconditions. Similarly,onmixed withwhenconditions are alsoORconditions.
on iddle
on iddleWill trigger once the browser has reached an idle state (detected using
requestIdleCallbackAPI under the hood).Is the default behavior.
on viewport
on viewportWill trigger when the specified content enters the viewport.
By default the
@placeholderwill 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 variablein the same template as@deferas the element that is watched to enver the viewport.
<div #greeting>Hello!</div>
@defer (on viewport(greeting)) {
<greetings-cmp />
}on interaction
on interactionWill trigger when the user interacts with the specified element through
clickorkeydownevents.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 variableas 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 hoverhovertriggers deferred loading when the mouse has hovered over the trigger area.Events used for this are
mouseenterandfocusin.
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 variableas the hover element.
<div #greeting>Hello!</div>
@defer (on hover(greeting)) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
}on immediate
on immediateTriggers 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 timerWould trigger after a specified duration
timer(x).The duration can be specified in
msors.
@defer (on timer(500ms)) {
<calendar-cmp />
}when
whenSpecifies a condition as an expression that returns a boolean.
When the expression becomes truthy, the placeholder is swapped with the content.
If the
whencondition 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.
whenandonare associated with defer controls when to render.prefetch whenandprefetch oncontrols 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
NgOptimizedImageThe
NgOptimizedImagedirective makes it easy to adopt performance best practices for loading images.Do not use background images in CSS, convert them to
absolutecomponents which can useNgOptimizedImage.
Getting Started
Enable the Directive.
<img ngSrc="cat.jpg">, replace thesrcattribute withngSrc.
Mark images as
priority.<img ngSrc="cat.jpg" priority>This will apply optimizations like:
fetchpriority=highloading=eagerAutomatically generate a
preload link elementif rendering on the server.
By default non-priority images will have
loading=lazy.
Include
widthandheight.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
fillattribute, you must not includewidthandheightattributes.For the fill image to render properly, its parent element must have
position: relative,position: fixedorposition: absolute.
<img ngSrc="cat.jpg" fill />Placeholder placeholder
placeholderNgOptimizedImagecan 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
base64without 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
placeholderwill also apply ablureffect 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 ExtensionGo to the
Profilertab 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,setTimoutoraddEventListener.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
OnPushchange 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
