Angular VS Lighthouse — Part 3

Francesco Cantarella
Geek Culture
Published in
4 min readMay 13, 2021

Tips & Tricks to increase your score.

This is part 3 of some useful tips and tricks to increase Lighthouse score and thereby reduce first page load time. You can find Part 1 here and Part 2 here.

High

If your project needs to download an external file translation (like using @ngx-translate and @ngx-translate/HTTP-loader and a .json translation file) then include this file into main.js bundle.

Without this technique, after Angular has started, the browser repaints the entire page when the download of the translation file is complete. So you will have a double page re-paint / re-layout that can cause a Cumulative Layout Shift (CLS) too.

Download translation file after Angular was started. Between SSR (server side rendering) and CSR (client side rendering) there are no translations provided so for each key there is a “” instead
Relative Lighthouse penalty in score and CLS

For include a translation file into main.js bundle follow these steps:
1) Create an environment file under environments folder for convenience:

env_lang.tsexport const env_lang = {
defaultLanguage: 'en',
inlineTranslations: require('../assets/i18n/en.json')
};

2) Create a service that implements TranslateLoader:

language-loader.tsconst translations = env_lang.inlineTranslations;@Injectable({providedIn: 'root'})
export class LanguageLoader implements TranslateLoader {
constructor(private http: HttpClient) {}
getTranslation(lang: string) {
if (translations && lang === env_lang.defaultLanguage) {
return of(translations);
} else {
return this.http.get(`./assets/i18n/${lang}.json`);
}
}
}

3) Provide LanguageLoader service into TranslateModule configuration (in app module imports):

app.module.tsimports: [
...
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useExisting: LanguageLoader,
deps: [PLATFORM_ID, HttpClient]
}
})
...
]
Translation file included into main causes no difference between SSR and CSR
No CLS! No wait for download another file! +10 points!

If your translation file is too large, try to include only the subset necessary for your optimized page and delay the download of the full file.
This is a not simple task but it’s very important to increase the score. If anyone is interested in how to implement it please leave a comment and I will prepare a dedicated article.

NOTE: In general, CLS can occur when loading the contents of a <div> (or similar) it changes the height or width of the div itself, so try to set a fixed size.
CLS can also occur when the font changes the size of the div.

Low/Medium

Often we organize and group the code for one topic, and it is a good practice. But if the page that we want to perform better use only a part then we must split into 2 different groups: one only for the minimum necessary, and one for the rest.
If another page use only a part of the first group the ideal is to split another time. This process can be done indefinitely, but it is at your discretion to repeat it several times as you will lose some ease in development phase.
This problem typically occurs in services where we put a lot of methods concerning a problem domain.

Low

Below you will find several small tips.

  • Remove old browser support:
.browserslistsrc> 0.5%
last 2 versions
Firefox ESR
not dead
IE 11 // Or remove totally IE support
  • Try to remove white spaces into HTML:
tsconfig.app.json{
...
"angularCompilerOptions": {
"preserveWhitespaces": false
}
...
}
  • Wrong import of rxjs class:
Bad
import {Subscription} from 'rxjs/index';
Good
import {Subscription} from 'rxjs';
  • If you have a large lookup table then split each key in a separate “export const”, and after generate one file per key:
lookup_data.tsexport const features = {
'feature1' : 1,
'feature2' : 2,
...
};
export const status = {
'new' : 0,
'active' : 1,
'closed' : 3,
...
};
...

Create a script for easily generate files:

generate_keys_file.tsconst fs = require('fs-extra');
import * as datas from './src/app/shared/lookup_data';
Object.keys(datas).map(value => {
fs.writeFileSync('./src/app/shared/' + value + '.ts' ,
'export const ' + value + ' = ' + JSON.stringify(datas[value]) + ';', {encoding: 'utf-8'});
});

After running this script you will have the features.ts, status.ts (etc) files so that you can only import what you need.

  • If you use moment js probably you don’t need his locales, so exclude they creating a webpack config file:
extra-webpack.config.tsconst webpack = require('webpack');
module.exports = {
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
]
};

Modify the angular.json file:

angular.json{
...
"projects": {
"myproject": {
...
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js",
"mergeStrategies": {
"loaders": "replace"
},
"replaceDuplicatePlugins": true
}
}
...
}

Lighthouse is in continuous development and over time has changed both the metrics and the relative scores and therefore also the page optimization process never ends.
I hope I have been helpful with these suggestions, and I invite you to leave comments and / or clarifications.

--

--