Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Excessive modulepreload Links in Angular 18.0.0-next.4 Affecting Performance Metrics #27490

Open
manzonif opened this issue Apr 17, 2024 · 5 comments

Comments

@manzonif
Copy link

manzonif commented Apr 17, 2024

Which @angular/* package(s) are the source of the bug?

platform-server

Is this a regression?

No

Description

I recently upgraded my Angular application to version 18.0.0-next.4 and migrated to esbuild and the new integrated SSR. However, I've noticed a significant impact on performance metrics, particularly First Contentful Paint (FCP) and Largest Contentful Paint (LCP), when using the new version.

Upon further investigation, I found that Angular now splits all JavaScript code into chunks and adds a <link rel="modulepreload"> for each chunk in the <head> of the page. Although the scripts are loaded asynchronously with @defer method, the preloaded scripts seem to be affecting the rendering of critical content, delaying FCP and LCP.

I've conducted tests using Lighthouse, and the results show that removing the modulepreload links improves FCP and LCP metrics. This issue seems to be similar to what was reported in these GitHub discussions:

GoogleChrome/lighthouse#11960
vitejs/vite#5991
In both discussions, users reported improvements in FCP and LCP metrics after disabling or reducing the number of modulepreload links.

Unfortunately, I couldn't find a way to selectively disable modulepreload links in Angular 18.0.0-next.4 based on specific use cases. Therefore, I would like to seek clarification and guidance on how to address this issue to improve performance.

Steps to Reproduce:

Upgrade an Angular application to version 18.0.0-next.4.
Enable SSR and use esbuild.
Analyze performance metrics using Lighthouse with and without the modulepreload links.
Expected Behavior:
I expect to see improved FCP and LCP metrics when excessive modulepreload links are removed or selectively disabled.

Additional Information:
I have attached screenshots of the Lighthouse test results, showing the impact of modulepreload links on performance metrics.

Screenshots:

Screenshot 1 - Lighthouse Test with modulepreload links

Screenshot 2024-04-17 132612

Screenshot 2 - Lighthouse Test without modulepreload links
Performed by removing all modulepreload links server side:

        html = html.replace(
          /<link .?rel="modulepreload" .*?href="(?<href>.+?\.js)".*?>/g,
          ''
        );

Screenshot 2024-04-17 132254

Your assistance in resolving this issue would be greatly appreciated.

Thank you.

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

No response

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 18.0.0-next.2
Node: 20.11.1
Package Manager: yarn 1.22.21
OS: win32 x64

Angular: 18.0.0-next.4
... animations, cdk, common, compiler, compiler-cli, core, forms
... google-maps, language-service, localize, material
... platform-browser, platform-browser-dynamic, platform-server
... router, service-worker

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.1800.0-next.2
@angular-devkit/build-angular     18.0.0-next.2
@angular-devkit/build-optimizer   0.1302.1
@angular-devkit/core              18.0.0-next.2
@angular-devkit/schematics        17.1.2
@angular/cli                      18.0.0-next.2
@angular/ssr                      18.0.0-next.2
@schematics/angular               18.0.0-next.2
rxjs                              7.8.1
typescript                        5.4.5
zone.js                           0.14.4

Anything else?

No response

@ngbot ngbot bot added this to the needsTriage milestone Apr 17, 2024
@atscott atscott transferred this issue from angular/angular Apr 17, 2024
@alan-agius4
Copy link
Collaborator

You can disable to preload tags by using the preloadInitial option.

  "projects": {
    "test": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:application",
          "options": {
            "outputPath": "dist/",
            "index": {
              "input": "src/index.html",
              "preloadInitial": false
            },

@manzonif
Copy link
Author

manzonif commented Apr 18, 2024

@alan-agius4 Thank you for your response and for providing the solution.

I would appreciate it if you would consider implementing support for selective filtering of module preloading links. I believe a feature like this would be incredibly valuable, especially for larger applications where fine-grained control over resource loading can have a significant impact on performance.

The implementation described in the Vite.js pull request #9938 seems to provide a robust solution by allowing users to define a resolveDependencies function to filter or modify the list of dependencies. This level of flexibility would empower developers to optimize resource loading based on specific use cases and performance requirements.

Are you planning to incorporate this functionality into Angular in the near future?
You might, for example, think about implementing it as part of the @defer block.

@alan-agius4
Copy link
Collaborator

@manzonif, I'm curious, how many preload links do you use?

Generally, offering such options doesn't really align with our design goals. Instead, we aim to provide a more responsive default that can cater to applications of all sizes.

There's definitely a correlation between Core Web Vitals (CWV) and preload tags. The more preload tags, the poorer the performance tends to becomes at least based on https://almanac.httparchive.org/en/2021/resource-hints#correlation-with-core-web-vitals

@manzonif
Copy link
Author

manzonif commented Apr 18, 2024

I count about 50 modulepreload links, plus an image that is part of the LCP. Of course it depends on the page being examined.

Furthermore, some of these modules, in turn, require the loading of third-party javascript (with the intention that they should be loaded late),perhaps making the situation even worse.

@manzonif
Copy link
Author

manzonif commented Apr 19, 2024

@alan-agius4, I also noticed that Angular preloaded modules take precedence in the document head over those inserted inside a component. If what @patrickhulce reports here is correct, the order of the preload hints is also important. Therefore, it would be necessary to ensure that the insertion of the module preloads is postponed, at least until after the ngOnInit lifecycle hook of the components.

If I add a preload for an LCP image, it should have high priority, IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants