angular / angular/angular-cli

Improve loading of 3P scripts

Aberta
#21,229 3 comentários 5 reações 1 responsável Reivindicada por @clydin Ver no GitHub
area: @angular-devkit/build-angular devkit/build-angular:browser feature
Linguagem predominante
TypeScript
Estrelas
27k
Forks
11.8k
Merge médio
14h 23min
PRs com merge (30d)
162

Descrição

# 🚀 Feature request

### Command (mark with an `x`)

- [ ] new
- [x] build
- [x] serve
- [ ] test
- [ ] e2e
- [ ] generate
- [ ] add
- [ ] update
- [ ] lint
- [ ] extract-i18n
- [ ] run
- [ ] config
- [ ] help
- [ ] version
- [ ] doc

### Description

Loading third-party scripts in a suboptimal way can cause performance regressions. A typical example is adding an SDK as script tag directly to the page head, which will block the loading of first-party scripts and delay hydration/CSR if done incorrectly.

### Describe the solution you'd like

Expand `scripts` the functionality already available in `angular.json`, allowing developers to configure different script loading strategies. Project [Aurora](https://web.dev/aurora) identified three script loading strategies that we can reuse:

- `afterInteractive` - this strategy would prioritize first-party scripts by executing third-party scripts after the page has been hydrated or client-side rendered.
- `beforeInteractive` - execute third-party scripts before first-party scripts. This strategy is particularly important for loading polyfills. Since we already have `polyfill.ts`, I'm a little hesitant to include it in the Angular CLI as part of the first feature iteration. I'd suggest keeping it out of scope and collecting feedback in the meantime.
- `lazyOnload` - lowest priority. We load these scripts and execute them in `requestIdleCallback`. Examples for libraries using this strategy for prefetching are [quicklink](https://github.com/GoogleChromeLabs/quicklink), [ngx-quicklink](https://github.com/mgechev/ngx-quicklink), and [guess-js](https://github.com/guess-js/guess).

We'd sometimes need a hook after the script has been loaded and executed if we load it asynchronously (`afterInteractive` or `lazyOnload`). For example, rendering a payment dialog after we've downloaded a third-party SDK.

Using `angular.json` does not provide an evident approach that would let us accomplish this. A lower-level API that would provide the necessary functionality is to allows developers to specify `id`'s in the script declaration. Using the `id`, developers will discover the `script` element in their code and hook to its `onload` event.

This way, the `scripts` property in `angular.json` would change from an array of strings or [objects](https://angular.io/guide/workspace-config#styles-and-scripts-configuration) to an array of strings or objects with the following properties:
- `input` - the source URL or path of the script. The CLI will perform different actions depending on whether `input` is a local path or a remote URL
- `strategy` - `afterInteractive`, `beforeInteractive`, or `lazyOnload`
- `inject` - specifies if the script should be injected or not. We preserve the current semantics of the `inject` property in the script object
- `bundleName` - applicable for local scripts only. For external scripts, we can throw an error
- `id` - an optional identifier that the developer can use to get a hold of the script and hook to its `onload` event

Suppose we find a string rather than an object for a given script. In that case, we can treat it as a script object with an `input` the specified string, `strategy` equal to `beforeInteractive` for the current major, or `afterInteractive` for the next major. Ideally, we'd want the default strategy to be `afterInteractive` to prevent folks from causing performance regressions, but since this would be a breaking change, we don't want to do this until v13.

In v13, we can migrate all the scripts from objects to strings using the `beforeInteractive` strategy.

### Describe alternatives you've considered

Project Aurora considered using a script component for alternative frameworks. Their approach easier accommodates the `onLoad` callback use case but seems to fit less naturally in the Angular CLI model.

Guia de contribuição

Abrir o guia de contribuição

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.