jss
jss copied to clipboard
1 issue and 1 question on Angular lazy loading withComponents
Description
Lazy loading of components is not working when using tsconfig paths import.
{ path: 'RichTextContent', loadChildren: () => import('@myrepo/shared/jss-components').then(m => m.RichTextContentModule) },
versus
{ path: 'RichTextContent', loadChildren: () => import('../../../../../libs/shared/jss-components/src/lib/rich-text-content/rich-text-content.module').then(m => m.RichTextContentModule) },
I do not get any errors in the first approach, but there aren't any seperate bundles generated for the components.
paths config:
"@myrepo/shared/jss-components": [ "libs/shared/jss-components/src/index.ts" ],
Expected behavior
Lazy loading should work with using paths configuration from tsconfig. This would prevent the need for manually navigating to a library folder.
Steps To Reproduce
See example above, load modules with @modulename import syntax.
Question on the use of lazy loaded components
I'm also wondering: how would configuring lazy loading for my Angular JSS App relate to this particular note on Server Side Rendering?
The JavaScript render engine creates one or more Node.js instances for each JavaScript module (based on module (file) path). Therefore, it is highly recommended to use a single bundle of components that can be invoked with a factory function. Otherwise, each bundle will require its own Node instance(s) which can grow to unmanageable levels.
https://jss.sitecore.com/docs/techniques/mvc-integration/javascript-rendering
It seems to me that, configuring lazy loading for JSS components would result in a lot of single bundle files. And thus, conflict with the above? What are the thoughts of the JSS team on this particular point?
Possible Fix
Your Environment
- JSS Version: 15.0.0
- Browser Name and version: Chrome latest
Screenshots
Without the use of paths (thus, using absolute folder locations):
With the use of paths from tsconfig
@EvtK I checked issue with lazy loading using our Angular sample. My steps to reproduce:
- Added lines to
src/tsconfig.app.json
:
"paths": {
"@angular/*": ["../node_modules/@angular/*"],
"rxjs": ["node_modules/rxjs", "../node_modules/rxjs"],
"@myrepo/shared/jss-components": ["app/components/shared-components.ts"]
}
- Created file
src/app/components/shared-components.ts
:
export { StyleguideAngularLazyLoadingModule } from './styleguide-angular-lazy-loading/styleguide-angular-lazy-loading.module'
- Changed dynamic import inside
src/app/components/app-components.module.ts
from
{ path: 'StyleguideAngularLazyLoading', loadChildren: () => import('./styleguide-angular-lazy-loading/styleguide-angular-lazy-loading.module').then(m => m.StyleguideAngularLazyLoadingModule) },
to:
{ path: 'StyleguideAngularLazyLoading', loadChildren: () => import('@myrepo/shared/jss-components').then(m => m.StyleguideAngularLazyLoadingModule) },
- Run Angular app without generation of component factory (in order to test current setup)
I'm navigating to /styleguide
from /
and I have a separate chunk:
I don't know all details about your env, maybe you have some changes in scripts/generate-component-factory
or something related to lazy-loading in angular.json
, but in original sample it's working, can you double check it?
About question related to Javascript Rendering
: https://jss.sitecore.com/docs/techniques/mvc-integration/javascript-rendering
It's deprecated since Oct 2020. I'm not sure that it makes sense to investigate something related to this topic
Hi @sc-illiakovalenko thanks for checking in and investigating. I have followed your sample and produce the same results. So I followed the same steps in my repo en then I noticed that the produced bundle for lazy loaded components is named after it's 'path' in tsconfig. This is also the case in the angular sample app.
as you can see here, the chunk is named my-repo-shared-jss-components
so then I decided to configure a second component as 'lazy loaded' in the jss sample module. I haven chosen the StyleguidTracking component for this. After serving the app (without component factory) this is my result:
as you can see: still one bundle, named after the library module (and doubled in size, because it now contains 2 components).
then I thought, what would happen if I remove the @ import notation:
yes, now we have 2 separate bundles again. So my conclusion. Yes on lazy loading with import notation a seperate bundle is generated, but it will allways be just one bundle (if using just one library ofcourse). That doesn't seem to match with the idea of lazy loading :). Are you willing to investigate this? Just to make sure: the reason this is important for us: we have a fairly large mono repo with over 30 apps en I guess we are heading towards a maybe 70 or 100 libs. To be able to import components/modules based on a lib path notation instead of doing {import('../../../../../libs/shared/jss-components/src/lib/rich-text-content/rich-text-content.module')
is a key in maintenance and keeping track in our repo!
Then for the question I have dropped on the Javascript Rendering. In the mean time we have had a online meeting with Nick and he has ellaborated some more on why this implementation is now deprecated. Thanks for the heads up!
@EvtK What about such proposal?
Define tsconfig.app.json
:
"paths": {
"@angular/*": ["../node_modules/@angular/*"],
"rxjs": ["node_modules/rxjs", "../node_modules/rxjs"],
"@myrepo/shared/jss-components/*": ["app/components/*"]
}
And define inside app-components.module.ts
:
loadChildren: () => import('@myrepo/shared/jss-components/styleguide-angular-lazy-loading/styleguide-angular-lazy-loading.module').then(m => m.StyleguideAngularLazyLoadingModule)
Will it fit for you? So you can reach every component using alias
@sc-illiakovalenko thanks for checking in again. I'm afraid it will not work. Our monorepo setup contains a large number of libraries on different levels which could all contain 'jss-components'. It would mean I have to manually add path alias references for importing this way, which is cumbersome and not easy to maintain.
Though, it seems I have stumbled upon an even bigger issue with the lazy loading setup. If I combine eagerly loading and lazy loading of jss-components, from the same library, there is no lazy chunk generated. Let me elaborate with an example:
I have a library 'jss-components' which exports 3 jss modules+components. Would look like this:
import { NavigationControlsComponent } from '@myrepo/shared/jss-components';
[..]
// eagerly loaded
[
{ name: 'NavigationControls', type: NavigationControlsComponent },
],
// lazy loaded
[
{ path: 'PageIntroDetail', loadChildren: () => import('../../../../../libs/shared/jss-components/src/lib/page-intro-detail/page-intro-detail.module').then(m => m.PageIntroDetailModule) },
{ path: 'RichTextContent', loadChildren: () => import('../../../../../libs/shared/jss-components/src/lib/rich-text-content/rich-text-content.module').then(m => m.RichTextContentModule) },
],
Now because the navigation controls component is eagerly loaded, it hits the barrel of the jss-components library which also exports the lazy loaded components. No chunks are generated! If I remove the import of the eagerly loaded NavigationControls, I do get a chunk produced.
This is actually blocking the implementation of the lazy loading, since we distribute all our components with the use of libraries :(. Of course this is not an issue with the setup in the angular jss sample app. Any thoughts on this?
We had this issue as well, and I don't think this is related to sitecore jss. If you eagerly import the barrel export in one spot, of course you'll end up including everything in that barrel file in the main bundle. If you want NavigationControlsComponent eagerly loaded, it needs to be in a separate library from your lazy components. We ended up creating lazy modules for every component, and only using barrel exports for pages that lazily load a guaranteed list of components.