With Ionic 3 we can change our Ionic apps to use lazy loading, which will significantly boost our performance. But we have to go through a few steps to migrate our old projects, which can get quite out of hand if you have a big project.
In this quick win we will go through the needed steps to upgrade your Ionic 2.x app to Ionic 3 so you can make use of Ionic lazy loading as well. I’ll also give you a script to automate most of the work in one step, if you have followed the general Ionic coding guidelines inside your app before!
1. Update Dependencies
First of all we need to update our package.json to use the new Ionic and Angular versions. Overall, you need to update the dependencies and devDependencies, but be careful not to replace other existing packages your app needs. Also, if you now already upgrade Ionic Native you have another part to take care of, so be aware which Ionic Native version you use!
Anyway, the updated fields of the file should look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
"dependencies": { "@angular/common": "4.0.0", "@angular/compiler": "4.0.0", "@angular/compiler-cli": "4.0.0", "@angular/core": "4.0.0", "@angular/forms": "4.0.0", "@angular/http": "4.0.0", "@angular/platform-browser": "4.0.0", "@angular/platform-browser-dynamic": "4.0.0", "@ionic-native/core": "3.4.2", "@ionic-native/splash-screen": "3.4.2", "@ionic-native/status-bar": "3.4.2", "@ionic/storage": "2.0.1", "ionic-angular": "3.0.1", "ionicons": "3.0.0", "rxjs": "5.1.1", "sw-toolbox": "3.4.0", "zone.js": "^0.8.4" }, "devDependencies": { "@ionic/app-scripts": "1.3.0", "typescript": "~2.2.1" } |
After updating this file, make sure to also update your packages by simply running:
1 2 |
rm -rf node_modules/ npm install |
Now your app might still work, but it’s not using lazy loading which we actually want, so follow the next steps to get this working in your app.
2. Create Modules for Pages & Use IonicPage
With Ionic 2 we had a folder for each page, with 3 files like in the image below.
Inside every of these folders we need to create a new file of the type NAME.module.ts, so the first file I would create inside the image is home-tabs.module.ts.
This new module file should look like this (if we follow the naming inside the image):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { HomeTabsPage } from './home-tabs'; @NgModule({ declarations: [ HomeTabsPage, ], imports: [ IonicPageModule.forChild(HomeTabsPage), ], exports: [ HomeTabsPage ] }) export class HomeTabsPageModule {} |
Nothing fancy, but we have to create this module file (with different names) for every page our old Ionic 2 app has!
Actually, near the end you will find a script that will do it for you if you have followed the general Ionic 2 coding and naming standards.
Next to creating this new module for every page, we also have to add a decorator to each page file. Make sure to import and add this to all of your pages:
1 2 3 4 5 |
import { IonicPage } from 'ionic-angular'; ... ... @IonicPage() @Component({ |
Now our pages are more or less ready to be used for lazy loading, but we also need to remove all the imports we had with Ionic 2 to actually use the lazy loading.
3. Remove Page Imports & Change Usage
In all of your pages, you can now go ahead and remove the import statements for other pages.
Especiall go through the app.module.ts and remove the imports, remove the pages from declarations and also from entryComponents.
This looks kinda scary but helps to boost the performance and startup time of your app!
Now how do we actually use pages now?
Quite easy!
Every time we used the page name before, we now use the page name in ” so as a string. Ionic will internally hook up everything and find the right module for your page.
For example what was before:
1 2 3 4 |
tab1 = MyClientsPage; tab2 = PlanningPage; tab3 = AvailabilityPage; tab4 = GeneralInformationPage; |
Now becomes:
1 2 3 4 |
tab1 = 'MyClientsPage'; tab2 = 'PlanningPage'; tab3 = 'AvailabilityPage'; tab4 = 'GeneralInformationPage'; |
Every push, setRoot or create of a new page which looked before like:
1 |
let modal = this.modalCtrl.create(ReplacementModalPage); |
Now simply becomes:
1 |
let modal = this.modalCtrl.create('ReplacementModalPage'); |
All of this can tike some time, but it’s worth it.
4. Add New Modules
A simple step, but needs to be mentioned:
We also need to import the BrowserModule into our app.module.ts. and if your App is using HTTP calls you also need to import the HttpModule like this:
1 2 3 4 5 6 7 8 9 |
import { BrowserModule } from '@angular/platform-browser'; import { HttpModule } from '@angular/http'; ... ... imports: [ BrowserModule, HttpModule, IonicModule.forRoot(MyApp) ], |
5. What about Pipes?
Good question, and this one was quite hard on the first try. I’m not 100% sure it’s the best solution, but it’s the only one working for me right now.
For all the pipes inside your app create one file next to them called pipes.module.ts, which will declare and export all your pies like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import { SortScheduleTime } from './sort-schedule-time'; import { SortEventTime } from './sort-event-time'; import { EventFilter } from './event-filter'; import { CustomDate } from './custom-date'; import { NgModule } from '@angular/core'; @NgModule({ declarations: [ CustomDate, EventFilter, SortEventTime, SortScheduleTime ], imports: [ ], exports: [ CustomDate, EventFilter, SortEventTime, SortScheduleTime ] }) export class PipesModule {} |
You can now also remove all imports and usages of your pipes inside your app.module.ts, they will be imported when needed!
If you now want to use a pipe in one of your pages, you simply add the new Pipes module to your imports of that page like this:
1 2 3 4 5 6 7 |
import { PipesModule } from './../../pipes/pipes.module'; .. .. imports: [ IonicPageModule.forChild(EventDetailsPage), PipesModule ], |
This is a bit initial migration work, but hey you want that ionic lazy loading and the goal is near!
6. What about Components?
Custom components should be treated exactly like pipes. Just create a new components.modules.ts next to your custom components folder, declare and export what you got and when needed import like in the pipes example before.
Additional, you need to import the IonicModule here to use Ionic components inside that module, like here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import { IonicModule } from 'ionic-angular'; import { CustomComponent } from './custom/custom'; import { NgModule } from '@angular/core'; @NgModule({ declarations: [ CustomComponent ], imports: [ IonicModule ], exports: [ CustomComponent ] }) export class ComponentsModule {} |
7. What about Providers?
All your providers will actually stay like they are. You don’t have to change anything here!
8. Having problems?
Sometimes you might encounter problems as pages are lazy loaded at later points and slow down your app at that time. In that case you might want to increase the priority for a page, check out the official documentation for setting the priority on how to set a higher priority for those pages (thanks to @BetaTools)!
Automate all the things!
Ok you might have noticed that a few steps above really need no brain, a monkey could do them as well.
I tried to outsource these steps into a little script, which will:
- Add IonicPage import and decorator
- Remove Page Import from other pages
- Remove imports, declarations and entryComponents from app.module.ts
- Create a NAME.module.ts for every page
- Change the usage of Pages to passing a string instead of the class
Although this script saved me some time (after I worked on it for about half a day) it might not work for you, so make sure to have your source code under version control or somehow backed up. If this script fucks up your project it’s not my fault.
Anyway, if you have used and generated Ionic 2 pages with the CLI and never changed something the script should work for you.
Inside your Ionic 2 project create a folder upgrade-script and insert 2 files:
- page-template.ts
- page-upgrade.sh
The template will be used to create the new module files, insert this into the templates file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { _PAGENAME_Page } from './_FILENAME_'; @NgModule({ declarations: [ _PAGENAME_Page, ], imports: [ IonicPageModule.forChild(_PAGENAME_Page), ], exports: [ _PAGENAME_Page ] }) export class _PAGENAME_PageModule {} |
Now the mighty script, insert this into the script file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#!/bin/bash cd ../src/pages # Add IonicPage import and decorator for i in **/*.ts; do echo "import { IonicPage } from 'ionic-angular';" | cat - $i > temp && mv temp $i sed -i '' 's/@Component/@IonicPage()@Component/g' $i done for d in *; do # Create the correct name of the Page parts=$(echo $d | tr "-" "\n") finalString="" for part in $parts; do upperCaseName="$(tr a-z A-Z <<< ${part:0:1})${part:1}" finalString=$finalString$upperCaseName done # Remove Page Import from other pages cd .. pageName=$finalString"Page" exclude="pages/$d/$d.ts" for f in $(find pages -type f -name "*.ts"); do if [ $f != $exclude ] then # Replace Page usage with 'Page' for lazy loading sed -i '' 's/'$pageName'/'\'$pageName\''/g' "$f" # Remove all imports of the page sed -i '' '/'$d'/d' $f fi done # back to correct folder cd pages # Copy the template file into the page folder cp ../../upgrade-script/page-template.ts "$d/$d.module.ts" # Replace the Placeholder inside the page template sed -i '' 's/_PAGENAME_/'$finalString'/g' "$d/$d.module.ts" sed -i '' 's/_FILENAME_/'$d'/g' "$d/$d.module.ts" # Remove imports, declarations and entryComponents sed -i '' '/'$pageName'/d' '../app/app.module.ts' done |
If you are a bash master and see improvements or want to change something, feel free to contact me!
Otherwise, I hope your upgrade to Ionic 3 lazy loading works fine with the above steps.
Yo can find a video version of this article below!
Get the free 7 day Ionic Crash Course to learn how to:
- Get started with Ionic
- Build a Tab Bar navigation
- Make HTTP calls to a REST API
- Store Data inside your app
- Use Cordova plugins
- Style your Ionic app
The course is free, so there's nothing you can lose!
I just finished migrating my app to Ionic 3.0.1 but I was stuck figuring out how to get my custom pipe to work. This saved me so much work, thank you!
I’d really enjoy learning about some of the benefits people have had before pursuing this change myself. How much faster did their app appear? Was startup time improved significantly? What are the drawbacks, if any? Can this be mixed? Do we have to convert all our pages – or can some be changed and others left to load resources the old way?
Agree with this reply. I would like to know why we spent time doing these changes, too.
The cost is high when our app gets bigger and these updates always takes a lot of time checking if everything is still working.
So far, amazing results! I reduced the load time on my app from more than 30 seconds to around 5-7 seconds.
If it’s taking that long, you are probably not compiling with –prod tag. Can reduce boot-time to 2-3 seconds
+1.
Why do you import BrowserModule into the module.ts files? Is it necessary?
[EDIT]
I misunderstood. I can see its imported into app.module.ts. At first I thought you imported it into all module.ts files 🙂
Yeah it’s only imported once, but it’s needed there since Ionic 3 🙂
Thanks for a nice tutorial btw. I’m currently transforming my app to use lazy loaded components. I had one minor issue though that some components were a bit slow when I needed them. Just found the documentation for preloading components. Maybe you could add it to your guide if it fits the purpose. http://ionicframework.com/docs/api/navigation/IonicPage/ Look at the bottom where it says “Priority” 🙂
Thanks, updated the post 🙂
Thanks for the tutorial. BTW the error you get at timestamp 12:00 in video (ioncardheader unknown) is because you in your components.module.ts file, you need to “import” IonicModule, so your custom components know of Ionic Components.
I’m having issues using custom components like it’s described here. I’ve opened an issue:
https://github.com/driftyco/ionic/issues/11560
Dat script “exclude” variable, tho’… 😉
I’m not really a scripting hero, I can sometimes bend it to my needs but it’s by no means good 😀
I am getting error sed: can’t read s/@Component/@IonicPage()@Component/g anyone solved this?
I am getting err when i import bunch of pages at once using single ngModule.
import { ListViewCloseButtonModule, ListViewSatisfiedButtonModule, ListViewReopenButtonModule } from “./close/close.component.module”;
@NgModule({
declarations: [ComplaintPage],
imports: [
ListViewSatisfiedButtonModule,
ListViewReopenButtonModule,
ListViewCloseButtonModule,
IonicPageModule.forChild(ComplaintPage)
]
})
export class ComplaintPageModule { }
I’ve stuck with component for a day and i found you, you saved my time a lot. Thanks mate.
Great work!!!
Save my day !!!
Best regards from Brazil !!!
hi simon i have checked your video.i have a question ? what about changing in main.ts/main.prod.ts, i have error in my main.prod.ts file after upgrade to ionic 3 from ionic 2 !’import { AppModuleNgFactory } from ‘./app.module.ngfactory’;’this line can’t be resolved
Is there any alternative to adding pipes / components module in every page
guys refer this https://docs.google.com/document/d/1vGokwMXPQItZmTHZQbTO4qwj_SQymFhRS_nJmiH0K3w/edit#
Hi. Thanks for the great article. I’ve followed your steps but I need to use a component in sidebar which is in app.component.html. so I’ve imported component.module in app.module but boom, failed. looks like we can’t import lazy-loading components in app.module?
Does it make any difference on android/ios app in performance if we use lazy loading in modules,
because we already have all chunks locally in device,?
I know it really makes difference on web but just wanted to know how it improved performance if we use lazy loading?
I had this working but now my components and pipes have stopped working (they just dont render anything). I upgraded to angular 5 and ionic 3.9. Not sure what the issue is? Can anybody shed some light?
What about dynamically created tabs with lazy loading. Root parameter of the tabs does not accept text?
Nice example of lazy-loading, but when I implemented lazy-loading, my ionic events (Event from ionic-angular) stops listening to published events. Can anyone please explain that why my ionic events stopped working as soon as I converted my ionic app into lazy-loading app?
I’ve also reported the issue on ionic’s github repo – https://github.com/ionic-team/ionic/issues/13955
Nice article. I’m facing an issue regarding – Ionic events which isn’t working correctly between lazy-loaded modules. Please check out my posted issue on github for full reference of this.
https://github.com/ionic-team/ionic/issues/14054
https://forum.ionicframework.com/t/ionic-events-not-working-correctly-between-lazy-loaded-modules/122096
Hi Simon Grimm Nice one as always. But i have issues to show alerts after implementing lazy loading as my code is below:
/Template:
Continue
Component alert function
showAlert() {
let alert = this.alertCtrl.create({
title: ‘New Friend!’,
subTitle: ‘Your friend, Obi wan Kenobi, just accepted your friend request!’,
buttons: [‘OK’]
});
alert.present();
}
Error:
https://uploads.disquscdn.com/images/1ffeec664e1363556493ed192dc08f29b7afb8e20d2a4b682c8a89c6c2a0852a.png