Thanks to latest releases it’s easy now with Ionic v3.x to build a PWA. But as shown on Lighthouse tool, out of the box performance is not extraordinary.
Measure performance
Performance is one of the most important points on the PWA checklist. To understand and improve performance we’ve decided to work on popular ionic starter blank. We’ll not add any feature, but follow some best pratices to try to reach better lighthouse performance rating. To not influence results with our local environment we’ll use lighthouse provided by cloud service webpagetest.org
Runtime environment
We’ve selected the Mobile Regular 3G
test configuration corresponding to runtime environment described below:
- User agent: Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4) Build/MPJ24.139-64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36
- Device Emulation Nexus 5X: Disabled
- Network Throttling 562.5ms RTT, 1.4Mbps down, 0.7Mbps up: Disabled
- CPU Throttling 4x slowdown: Disabled
On this post we’ll create a new Ionic project from blank starter and measure benefits of each tips recommended to improve performance. These tips could significantly improve 1st load time, reducing css or main.js, but for next loads performance is driven by the use of service-workers responsible for cache.
Setup project
local env
I’ve upgraded the cli to next major release Ionic v4, some commands could be slightly different on v3.
$ ionic info
cli packages: (/usr/local/lib/node_modules)
@ionic/cli-utils : 2.0.0-rc.5
ionic (Ionic CLI) : 4.0.0-rc.5
local packages:
@ionic/app-scripts : 3.1.9
Ionic Framework : ionic-angular 3.9.2
Start blank project
$ ionic start meu-starter.ionic-v3
? Project type: ionic-angular
? Starter template: blank
? Would you like to integrate your new app with Cordova to target native iOS and Android: No
? Install the free Ionic Pro SDK and connect your app: No
Setup PWA
Remove cordova.js and enable service worker
Update src/index.html
<!-- cordova.js required for cordova apps (remove if not needed)
<script src="cordova.js"></script> -->
<!-- un-comment this code to enable service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>
And finally run the build with ionic cli $ ionic build --prod
and deploy the app on firebase hosting to test it with Lighthouse.
Lighthouse audit report
Ionic v3: PWA Performance |
---|
$ ls -slh www/build| awk '{print $6,$10}'
420K main.css
5.4K main.js
95K polyfills.js
15K sw-toolbox.js
528K vendor.js
$ du -ach www/assets/fonts | grep total
2.2M total
Optimization-1: Build for one platform only
Ionic uses modes to customize the look of components. Each platform has a default mode, but this can be overridden. For example, an app being viewed on an Android platform will use the md
(Material Design) mode. The <ion-app>
will have class="md"
added to it by default and all of the components will use Material Design styles:
<ion-app class="md">
When building a PWA you use only one mode for all web browser, then no need to import all modes.
Open src/app/app.module.ts and replace:
IonicModule.forRoot(MyApp)
by:
IonicModule.forRoot(MyApp, {
mode: 'md' // 'md' | 'ios' | 'wp'
})
Make a copy of @ionic/app-scripts/config/sass.config.js
and update it to exclude ios and wp styles.
$ mkdir config
$ cp node_modules/\@ionic/app-scripts/config/sass.config.js config/.
$ vi config/sass.config.js
...
excludeFiles: [
/\.(wp|ios).(scss)$/i,
],
...
And add this new config file to package.json
"config": {
"ionic_sass": "./config/sass.config.js"
},
www/build optimization-1 | www/build starter blank |
---|---|
236K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js |
420K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js |
Optimization-2: Remove iOS fonts
Open src/theme/variables.scss
and remove @import "noto-sans";
And add this new config file to package.json
"config": {
"ionic_sass": "./config/sass.config.js",
"ionic_copy": "./config/copy.config.js"
},
www/build optimization-1+2 | www/build starter blank |
---|---|
235K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js 1016K www/assets/fonts |
420K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js 2.2M www/assets/fonts |
Optimization-3: Custom icons
Build custom icons lib with only the icons you need.
To remove default ionicons
open src/theme/variables.scss
and remove @import "ionic.ionicons";
HOW TO USE CUSTOM ICONS ON IONIC 3
$ ls -slh www/build| awk '{print $6,$10}'
195K main.css
5.4K main.js
95K polyfills.js
15K sw-toolbox.js
528K vendor.js
Optimization-4: Remove useless CSS
A lot of styles on css bundle are for components that we do not even use.
Open sass.js
and modify the excludeFiles
section once again with the following:
excludeFiles: [
/\.(wp|ios).(scss)$/i,
/(action-sheet|alert|backdrop|badge|button|card|checkbox|chip|datetime|fab|grid|icon|img|infinite-scroll|input|item|label|list|loading|menu|modal|note|picker|popover|radio|range|refresher|searchbar|segment|select|show-hide-when|slides|split-pane|spinner|tabs|toast|toggle|virtual-scroll|cordova)/i,
],
For the blank
app we only need to keep: app
, content
and toolbar
but of course in a more advanced app you will need more.
www/build optimization-1+2+3+4 | www/build starter blank |
---|---|
86K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js 1016K www/assets/fonts |
420K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js |
Remove unused $colors
Remove all unused colors from the $colors array in src/theme/variables.scss
. Ideally you will only have one or two colors left.
$colors: (
primary: #488aff,
secondary: #32db64
);
www/build optimization-1+2+3+4 | www/build starter blank |
---|---|
65K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js 1016K www/assets/fonts |
420K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js |
Optimization-5: run purify-css
$ npm install purify-css -g
$ purifycss www/build/main.css www/build/*.js --info --min --out www/build/main.css
________________________________________________
|
| PurifyCSS has reduced the file size by ~ 53.5%
|
________________________________________________
www/build optimization-1+2+3+4+5 | www/build starter blank |
---|---|
30K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js 1016K www/assets/fonts |
420K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js |
Optimization-6: Removing Cordova plugins
$ npm uninstall --save @ionic-native/core @ionic-native/splash-screen @ionic-native/status-bar
Remove import and providers declarations from src/app/app.module.ts:
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
providers: [
StatusBar,
SplashScreen,
...
]
Do same job on src/app/app.component.ts
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { HomePage } from '../pages/home/home';
@Component({
templateUrl: 'app.html'
})
export class MyApp {
rootPage:any = HomePage;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
});
}
www/build optimization-1+2+3+4+5+6 | www/build starter blank |
---|---|
30K main.css 5.3K main.js 95K polyfills.js 15K sw-toolbox.js 514K vendor.js 1016K www/assets/fonts |
420K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js |
Optimization-7: Add lazy-loading
Ionic and Lazy Loading Pt 1 Ionic and Lazy Loading Pt 2
www/build optimization-1+2+3+4+5+6+7 | www/build starter blank |
---|---|
5.0K 0.js 30K main.css 3.8K main.js 95K polyfills.js 15K sw-toolbox.js 514K vendor.js 1016K www/assets/fonts |
420K main.css 5.4K main.js 95K polyfills.js 15K sw-toolbox.js 528K vendor.js |
Conclusion
this issue is going to be solved without having to do any of the build magic that Julien was doing. In a future release of Ionic-Angular every component will lazy load only the css needed at that time. So if its on an Android device or on the web as a PWA it will only lazy-load the css needed for MD mode, and none of the other mode’s css will get loaded. Also, as i said, its going to be lazy loaded per component, so you are only ever loading the css needed specifically for the components on that page. So, while this is a very cool setup, in the near future it will not be needed anymore.
Source: github.com/ionic-team/ionic-app-scripts