jss icon indicating copy to clipboard operation
jss copied to clipboard

1 issue and 1 question on Angular lazy loading withComponents

Open evtk opened this issue 4 years ago • 5 comments

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): image

With the use of paths from tsconfig image

evtk avatar Nov 02 '20 08:11 evtk

@EvtK I checked issue with lazy loading using our Angular sample. My steps to reproduce:

  1. 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"]
    }
  1. Created file src/app/components/shared-components.ts:
export { StyleguideAngularLazyLoadingModule } from './styleguide-angular-lazy-loading/styleguide-angular-lazy-loading.module'
  1. 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) },
  1. 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: image image

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

sc-illiakovalenko avatar Nov 04 '20 08:11 sc-illiakovalenko

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.

image

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:

image

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:

image

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 avatar Nov 06 '20 13:11 evtk

@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 avatar Nov 06 '20 14:11 sc-illiakovalenko

@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?

evtk avatar Nov 09 '20 09:11 evtk

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.

apoteet avatar Dec 14 '21 16:12 apoteet