feat(animations): Provide a way to lazy load the animations#50738
feat(animations): Provide a way to lazy load the animations#50738JeanMeche wants to merge 3 commits intoangular:mainfrom
Conversation
1e600bb to
9eb4b94
Compare
9eb4b94 to
bc81f84
Compare
22e43cc to
a3b6b41
Compare
7456abb to
c773b6c
Compare
7766964 to
52424c2
Compare
a76ee9c to
5b8836b
Compare
7acd70a to
62da920
Compare
AndrewKushnir
left a comment
There was a problem hiding this comment.
@JeanMeche thanks for making progress on this PR, leaving some comments after the initial review with @jessicajaniuk.
There was a problem hiding this comment.
In this scenario the NoopAnimationDriver and NoopAnimationStyleNormalizer would become non-tree-shakable (as they are eagerly referenced). Can we refactor the code to avoid this?
There was a problem hiding this comment.
@JeanMeche as a possible solution, we can mimic the provideNoopAnimations() function and call it provideNoopAnimationsAsync(), so that we create one of the engines depending on the function used.
There was a problem hiding this comment.
About that, I thought about your suggestion. I doesn't seem applicable to me.
createEngine(), the drivers and the styleNormalizers are part of the lazy loaded bundle so we cannot use providers.
There was a problem hiding this comment.
Could you please elaborate a bit on this? My assumption is that having the provideNoopAnimationsAsync() function would allow us to reference the noop-related code there, so we don't have to do it in the provideAnimationsAsync() function. As an option, we can have a callback there that we define there:
useFactory: (doc: Document, renderer: DomRendererFactory2, zone: NgZone) => {
// in `provideAnimationsAsync`:
const getEngine = (m) => m.getEngine();
// in `provideNoopAnimationsAsync`:
const getEngine = (m) => m.getNoopEngine();
return new AsyncAnimationRendererFactory(doc, renderer, zone, type, getEngine);
},
There was a problem hiding this comment.
Sure,
NoopAnimationDriver/NoopAnimationStyleNormalizer are part of the animation module, the module we want to lazy load. By referencing anything of that module outside of it, we will break the code splitting/lazy loading, so we can't define them in the providers.
Currently createEngine() is not imported statically but via the dynamic import of @angular/animations/browser. That why it doesn't break the lazy loading.
I have tried using the animationType parameter to determine which of createEngine or createNoopEngine to call. But both builder don't seem able to infer that animationType is readonly in AsyncAnimationRendererFactory (and it doesn't tree shake the unused one). (demo)
Also, I did try your other solution with the callback, It did not work, likely because the lazy loaded module is passed to the callback function, so the minifer doesn't know what to keep / what to tree-shake.
packages/animations/browser/src/render/web_animations/web_animations_driver.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Can we extract this into a separate PR?
There was a problem hiding this comment.
There is definitely some work to do on this.
Pulling the BrowserAnimationBuilder breaks the code splitting because it's in the platform-browser/animations (and not in the async secondary entry we had to created).
I'll remove this from the scope of this PR.
packages/platform-browser/lazy-animations/src/async_animation_renderer.ts
Outdated
Show resolved
Hide resolved
packages/platform-browser/lazy-animations/src/async_animation_renderer.ts
Outdated
Show resolved
Hide resolved
packages/platform-browser/lazy-animations/src/async_animation_renderer.ts
Outdated
Show resolved
Hide resolved
caa720c to
f372012
Compare
|
@AndrewKushnir @jessicajaniuk Thank you both for your feedbacks ! I have a great news, for a reason I could not determine, the import of the core module in not an issue anymore when using the webpack builder ! |
AndrewKushnir
left a comment
There was a problem hiding this comment.
I have a great news, for a reason I could not determine, the import of the core module in not an issue anymore when using the webpack builder !
This is great 🎉
There was a problem hiding this comment.
@JeanMeche as a possible solution, we can mimic the provideNoopAnimations() function and call it provideNoopAnimationsAsync(), so that we create one of the engines depending on the function used.
There was a problem hiding this comment.
@JeanMeche thanks for addressing the feedback!
I'm also thinking that it'd be great to add a new integration testing app to have a way to capture the bundle size in the size tracking golden files (so that we confirm that everything works as expected and there are no regressions in the future). We can copy the integration/animations app and update it to use the provideAnimationsAsync function there.
There was a problem hiding this comment.
Could you please elaborate a bit on this? My assumption is that having the provideNoopAnimationsAsync() function would allow us to reference the noop-related code there, so we don't have to do it in the provideAnimationsAsync() function. As an option, we can have a callback there that we define there:
useFactory: (doc: Document, renderer: DomRendererFactory2, zone: NgZone) => {
// in `provideAnimationsAsync`:
const getEngine = (m) => m.getEngine();
// in `provideNoopAnimationsAsync`:
const getEngine = (m) => m.getNoopEngine();
return new AsyncAnimationRendererFactory(doc, renderer, zone, type, getEngine);
},
|
@AndrewKushnir I've added the integration tests and checked, they fail when the code split/lazy loading is broken. |
AndrewKushnir
left a comment
There was a problem hiding this comment.
@JeanMeche thanks for addressing the feedback, the change looks great! 👍
`provideLazyLoadedAnimations()` returns providers which allow the lazy loading of the animation module. Lazy loading of the animation code can shave off up to 16KB gzipped of the main bundle.
This integration test aims to cover that we do not break the code splitting of the animation module when we use `provideAnimationsAsync()`.
Let's have the same test app for async and eagerly loaded animations.
There was a problem hiding this comment.
The issue was resolved in #51910. Once we have G3 issues resolved, this is good to go.
reviewed-for: fw-animations, fw-core, integration-tests, public-api, size-tracking
dylhunn
left a comment
There was a problem hiding this comment.
reviewed-for: public-api
|
TGP is green. This is ready to merge. |
|
This PR was merged into the repository by commit 81e7f5b. |
|
was it renamed to |
|
@robertIsaac yes, it was. |
|
I tried it, but saw size increases instead of decrease |
|
@robertIsaac Please file an issue with the repro and we can take a look. |
|
@robertIsaac Please open an new issue with a repro repo and we'll happily investigate that 😊 |
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
As part of #50399, this work here is a proposition to allow the lazy loading of animations code that can shave off up to 16KB gzipped of the main bundle !
The PR introduces a new entry point in the
platform-browserpackage :lazy-animations. This has been necessary because the eager providers (BROWSER_ANIMATIONS_PROVIDERSandBROWSER_NOOP_ANIMATIONS_PROVIDERS) were retaining symbols from@angular/animation/browsermaking the code splitting & lazy loading impossible.In this entry point, you will find the new
provideLazyLoadingAnimations()function which will provide a newRendererFactory2:AsyncAnimationRendererFactory.AsyncAnimationRendererFactorywill create a newDynamicDelegationRendererwhich will delegate to the default renderer by default.Aside from the renderer, the new factory will lazily import the
@angular/animations/browser. Once the package is loaded, the factory will switch the delegate ofDynamicDelegationRendererto anAnimationRenderer.The side effect of this implementation is that the rendering might not include the styles from the animations defined in components until the module is loaded. This should an acceptable drawback.
Also, I'd like to thank @AndrewKushnir for his help on this PR.
PR Type
Does this PR introduce a breaking change?