Clarification on Ionic /components folder

The components I need are very common, used across lots of pages in the app. If I make those pages lazy loaded, they have their own page.module.ts, which is how ionic identifies it and makes it into a deep link. The problem with that is, I can’t just include my components.module.ts in the top level app.module.ts because then each page does not get access to them (because they are each their own module, that’s just how it works unfortunately). So I have to include my components module in each page’s module, which duplicates the code.

That’s not entirely accurate. If you include your shared module in the app module so they are eagerly loaded they will not be duplicated into each lazily loaded module. The code duplication is only an issue if you only import via lazy loading and not in the app module. That’s why grouping by feature and having one shared module fixes this.

EDIT: “fixes this” isn’t entirely accurate, more accurately is “makes it less of an issue because components are only loaded with the modules that use them”.

Yea, I’m not debating that eagerly loading them in app.module.ts does not duplicate them. The problem is, if the page uses a component, it needs to import the components.module.ts itself. Importing it in app.module.ts does not give the lazy-loaded page modules access to the components included by importing components.module.ts in app.module.ts. So if I choose to lazy-load it will duplicate code (unless we find a common chunks solution).

If I chose not to lazy load, and eager loaded the pages just as components (without the need for a pageName.module.ts for each), then yes, including components.module.ts in app.module.ts would work, but I needed the lazy-loading setup.

Sorry, I guess I don’t understand. You can absolutely still lazy load your pages with this and not get duplicate code. You are only eagerly loading components that are shared app wide, which makes sense to do anyway. Then lazily load pages and any components that are specific to a feature/page.

The setup for each lazy-loaded page includes a page.ts, page.html, and page.module.ts. All the module does is:

@NgModule({
  declarations: [Page],
  imports: [IonicPageModule.forChild(Page)],
  entryComponents: [Page]
})

If I do this, and import my components.module.ts top-level at app.module.ts, I cannot use any components from components.module.ts in “Page”. I must also add “ComponentsModule” to the list of imports in the NgModule above. If I do this, it duplicates the code ComponentsModule code in every page I import it in with the code splitting.

You are absolutely right about this part:

You are absolutely wrong about this part (If you have already imported your components module in the app module):

Try importing it into the app module and then go look at your resulting split code files. It wont be duplicated in there and will instead be in the main js file.

It does though. I just verified. Every page’s code split file includes the entire contents of components.module.ts, despite it being included app.module.ts as you suggest.

I’m seeing the same behavior as @rlouie is describing here. I was curious about this, and started looking at my bundles. If I have my SharedComponentModule imported in the root module, and imported in my child modules (pages), it appears the shared module is only bundled into main.js, and not the lazy chunks (0.js, 1.js, 2.js, etc).

I tried to verify by importing lodash inefficiently in my shared ocmponent module, and lodash is only included in the main.js. (the circled blocks are my lazy component modules)

If i DO NOT import the shared module in the root, I get a copy in each of my lazy loaded modules, as I would expect (they have to since there’s no graph for them to expect the shared module to have already been loaded).

EDIT: To clarify, I added lodash to show a large bundle size, otherwise my custom components are too small to distinguish if anything is being duplicated

1 Like

Very interesting. Is lodash coming from node_modules? Because if so, node_modules files are included in the ionic common chunk plugins they use, so by default those are considered common and not inlined. Did you follow the lazy-loading setup described in the google doc? I followed those and when I include ComponentsModule in both app.module.ts and any page’s module.ts, it is still duplicated in each page’s split file.

EDIT: Also, what visualizer are you using? I’d like to use that instead of perusing each chunk file which is obnoxious :slight_smile:

lodash is coming from node_modules, and I inefficiently imported it in a SharedComponent like:

import * as _ from 'lodash';

I didn’t follow a google doc, I just looked at this repo https://github.com/mhartington/star-track-ionic/tree/master/src and setup my page modules the same. Basically, I have each page in my app as a module, a sharedComponentModule, and a root module (app.module)

I import the shared component module in the root module, and reference all routes as strings instead of imported Types, and let the DeepLinker magic do its thing.

EDIT: @calpoog I replied to myself, whoops

The visualizer is https://www.npmjs.com/package/webpack-bundle-analyzer

You can inject it by:

Npm install the plugin

npm install webpack-bundle-analyzer --save-dev

Edit package.json and add:

...
"config": { 
    "ionic_webpack":"./config/webpack.config.js"
}
...

Add a new folder and file “config/webpack.config.js”

Add this code to webpack.config.js:

const webpackConfig = require('../node_modules/@ionic/app-scripts/config/webpack.config');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

webpackConfig.plugins.push( new BundleAnalyzerPlugin({
    analyzerMode: 'static',
    generateStatsFile: true
}))

Thanks for the plugin. Unfortunately it’s not helping me because it says it can’t parse 0.main.js. Apparently it can parse the rest of them because the output still generates. Then I don’t see my ComponentsModule in any of the output, main, or split files… Not sure what the issue is there.

@barryrowe I really appreciate your contributions here. There might be a way to thread this lazyloading needle after all! Interested to see if these contradictions really are contradictions, or if it’s as “simple” different people setting up their webpacks differently.

@barryrowe

It actually seems to have fixed itself… Strangely, I deleted all the files in /build (it was numbering in the 20s) and restarted ionic completely… And now it’s only outputting 11 chunks. My guess is somewhere along the way it started getting messed up and the files I was viewing manually were older versions, and some chunks were duplicated and newer versions of others? However, I still can’t see my components module showing up in the visualizer anywhere. I think that’s going wrong somewhere because it says it can’t parse 0.main.js (but that’s a very simple basically empty page’s output…) I guess I’ll accept this as a “I don’t know why but I won’t challenge it” type of bug fix. Although, I really would like to get this visualizer working properly so I can see where ComponentsModule truly is to validate it’s working as you showed above.

As @barryrowe says, this is the behavior. I even went back and double checked against @rapropos original github repo here: https://github.com/rapropos/ionic-lazy-sandbox

If you npm install then ionic serve that and go look in 0.main.js you can see the shared module is present:

However, if you then go import it into app.module.ts webpack will rebuild and you can try to search in 0.main.js again:

You can see it’s gone. Instead it’s in main.js. So again, this is why you group by feature. Anything that should be shared app wide is a good candidate for eager loading anyway, so this part works fine. Then just only import more specific components into their respective feature module and they’ll lazy load. The duplication of code should be very minimal.

Just wanted to post this so you knew it worked equally as well with regular old angular modules and not just npm modules.

1 Like

Could you expand this a bit? I know you’ve done it before, but briefly, what do you think is best practice for folder structure / lazy loading? Thank you.

Yea, the issue wasn’t the concept, it was the execution. Something in ionic’s build process is messed up if running through watch for a while. It was generated double the number of build output chunks as it should have been, some of them being old and some being new, half showing continued inlining of components that should have been eagerly loaded.

2 Likes

It’s tough because Ionic doesn’t support it properly due to only lazy loading individual pages. But despite that I still believe you should group by feature. This will still let you lazily load whole modules for feature that only have one page. I consider that a pretty big limitation, but Ionic says they are going to start supporting this, and I don’t want to sacrifice architecture for what should be a temporary problem.

I would suggest simply reading the Angular style guide. Remember that Ionic (for the most part) is just a UI framework on top of Angular. It definitely does some other useful stuff too, but the foundation is still Angular. https://angular.io/docs/ts/latest/guide/style-guide.html

This style guide is the official Angular way of doing things, and goes over in detail lots of stuff about app structure. If you follow that it’s hard to go wrong.

EDIT: To expand, if you look at my example earlier in the thread of the profile module. That had a profile page, a component, and a service. All of those things would be lazy loaded, not just the page. You could then also reuse that profile module in any other project by simply copy and pasting the folder. It also makes it very easy to find the code you want because in your app you’re on the profile page, so when you see a folder named “profile” at the top level and it contains all your profile code…well, there’s no mystery there. You don’t have to hunt through a giant list of components or pages. Again that’s all in the style guide, but figured it might help to tie back into my earlier example.

Ah gotcha, strange. That accounts for it though. Glad you got to the next step at least. I know it’s not the perfect solution but with some tweaking it should get you close enough until the build process is updated.

I was afraid of that. My folder structure is 100% produced by Ionic CLI generators. Looks as though I ought to do some restructuring to prep for lazyload. Thanks. I wonder if the CLI generators are going to change in a future release,to accommodate this.

1 Like