angular-movies-perf's People
angular-movies-perf's Issues
Update RxJS to v7
Remove MatButton and MatIcons
remove form field
Setup Linting Rules
OnPush:
- setup linting
- adopt components
- config schematics
trackBy:
- setup linting
- adopt components
unused variables:
- setup linting
- adopt components
Fix inconsistencies and bugs through rewrite
- login logout state is not reflected in the menu
- Movie hover styling misses gray background in the description area
- login success shows infinite loading spinner
- click on movie does not navigate to details page
Categories Delete Modal
Styling
The categories component includes empty styles.scss file. This causes extra overhead in the build time and increases the bundle size.
- Solution:
Remove the styles file and import in the component
MovieTrailerComponent
- reduce DOM nesting
- preconnect domains of iframe
- lazy load iframe native-iframe-lazy-loading
Organize for better bundling
To get smaller bundle sizes the Angular Module and lib structure need to get refactored.
Refactorings:
- Refactored to SCAMs
- Get rid of
SharedMaterialModule
and organize in SCAMs - Introduce app shell which only provides the minimum and the rest can be preloaded
Implement movies details page
Account Page
Images
The avatar image has no dimensions set. This causes CLS and can be avoided by providing an aspect ratio to the IMG tag.
Furthermore, we can use native lazy loading to give the Browser time to process more important tasks.
<img *ngIf="photoURL" src="{{photoURL}}" alt="profile">
Expected Impact:
The Browser can process more important tasks first. When the image gets rendered no CLS will occur.
- Solution:
<img *ngIf="photoURL" src="{{photoURL}}" alt="profile" loading="lazy" width="50" height="50">
Styling
In the account page the username is used to fill the image if no avatar is given. This value is used in the template.
The drawback here is Angular will need to reevaluate the template to apply the character to the DOM which is expensive.
I suggest moving the logic which adds a character to the DOM to JS only. In addition, I added a small enrichment for the case no avatar is given. I use the first letter of the username to derive a random color for the background of the avatar image to make it look a little nicer.
Expected Impact:
Both things are done by using CSS variables. In this way, we can avoid change detection.
- Solution:
@Component({
template: `
<!-- Template not needed to accomplish style adoption -->
`,
styles: `
:root {
--avatarColor: #cccccc;
}
.avatar {
background-color: var(--avatarColor);
}
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class Component {
change() {
document.documentElement.style.setProperty('--avatarColor', '#cccccc');
}
}
Third party libs
As dayjs
is used only once to get the proper date format we can remove it and use vanilla JS.
Usage:
this.creationTime = dayjs(authData.metadata.creationTime).format('YYYY-MM-D');
Expected Impact:
Checking bundlephobia dayjs
has a total of:
-
6.3kB MINIFIED
-
2.8kB MINIFIED + GZIPPED
-
Solution
Where I would question the format, in particular, the last single "D" as correct the value is never used anywhere so I would drop it completely.
State Management
At the moment the component maintains a subscription to the user data stream. This leads to cluttery code and usage of a lot of workarounds like the need for markForCheck
.
A refactor to template-based subscription handling will reduce the code base and enable future performance optimizations with @rx-angular/template
Expected Impact:
We can reduce LOC, the number of injected services which will reduce bootstrap time and prepare for further render improvements.
- Solution:
use the ngIf hack as the first quick win before introducing rx-angular.
Aligne UI and features with https://github.com/tastejs/next-movies
Implement global state to cache results
Introduce global state to cache results between routes.
Furthermore, it introduces a new layer so subscriptions are not owned by the components, but rather in a central place.
- Intro state service
- reduce subscriptions handling code by using tools that subscribe in the template (async, push, let)
Setup git hooks with husky, lint-staged and commitlint
Toods:
- setup prittier
- setup lint-staged
- setup commitlint
- setup husky
- add pre-commint hook
- add commint-msg hook
- setup inteligent commitlint preset
- add contributors.md
Remove Animations Module
Image lazy code is slow
Initial linting report
-
src/app/about/about.component.ts:3:1
- ERROR: 3:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/account/account-delete-modal/account-delete-modal.component.ts:4:1
- - ERROR: 4:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/account/account.component.ts:11:1
- - ERROR: 11:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/app.component.ts:9:1
- - ERROR: 9:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
- - ERROR: 31:61 no-explicit-change-detection-apis Use with caution. detectChanges is synchronous and will trigger change detection in this component and all its children.
- - ERROR: 49:31 prefer-no-layout-sensitive-apis Use with caution. scrollY may trigger style recalculation
- - ERROR: 55:16 prefer-no-layout-sensitive-apis Use with caution. scrollTo may trigger style recalculation
-
src/app/categories/categories-add-modal/categories-add-modal.component.ts:5:1
- - ERROR: 5:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/categories/categories-delete-modal/categories-delete-modal.component.ts:4:1
- - ERROR: 4:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/categories/categories.component.html:29:22
- - ERROR: 29:22 template-use-track-by-function Missing trackBy function in ngFor directive
- - ERROR: 87:13 template-use-track-by-function Missing trackBy function in ngFor directive
- - ERROR: 89:22 template-use-track-by-function Missing trackBy function in ngFor directive
-
src/app/categories/categories.component.ts:13:1
- - ERROR: 13:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/movies/movie-list/movie-list.component.ts:8:1
- - ERROR: 8:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/movies/movie/movie-trailer/movie-trailer.component.ts:4:1**
- - ERROR: 4:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/movies/movie/movie.component.html:46:14
- - ERROR: 46:14 template-use-track-by-function Missing trackBy function in ngFor directive
- - ERROR: 83:14 template-use-track-by-function Missing trackBy function in ngFor directive
- - ERROR: 103:26 template-use-track-by-function Missing trackBy function in ngFor directive
- - ERROR: 131:30 template-use-track-by-function Missing trackBy function in ngFor directive
-
src/app/movies/movie/movie.component.ts:20:1
- - ERROR: 20:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
- - ERROR: 141:11 no-unused-variable 'dialogRef' is declared but its value is never read.
-
src/app/movies/movies.component.ts:11:1
- - ERROR: 11:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/playlist/playlist.component.html:9:22**
- - ERROR: 9:22 template-use-track-by-function Missing trackBy function in ngFor directive
- - ERROR: 70:22 template-use-track-by-function Missing trackBy function in ngFor directive
-
src/app/playlist/playlist.component.ts:10:1
- - ERROR: 10:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/settings/settings.component.html:7:22
- - ERROR: 7:22 template-use-track-by-function Missing trackBy function in ngFor directive
-
src/app/settings/settings.component.ts:7:1
- - ERROR: 7:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/shared/component/pagination/pagination.component.html:14:10
- - ERROR: 14:10 template-use-track-by-function Missing trackBy function in ngFor directive
-
src/app/shared/component/pagination/pagination.component.ts:3:1
- - ERROR: 3:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/shared/component/share-modal/share-modal.component.ts:6:1
- - ERROR: 6:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/shared/service/tmdb/tmdb.service.ts:23:49
- - ERROR: 23:49 no-unused-variable Property 'storageService' is declared but its value is never read.
-
src/app/sign-in/sign-in.component.ts:7:1
- - ERROR: 7:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
-
src/app/star/star.component.html:18:26
- - ERROR: 18:26 template-use-track-by-function Missing trackBy function in ngFor directive
- - ERROR: 31:26 template-use-track-by-function Missing trackBy function in ngFor directive
-
src/app/star/star.component.ts:13:1
- - ERROR: 13:1 prefer-on-push-component-change-detection The changeDetection value of a component should be set to ChangeDetectionStrategy.OnPush
Remove zone.js
By removing zone.js
we can save significant bundle size and bootstrap time.
Things I had to change:
- routing and change detection (introduced helper service)
- refactor
async
pipe torxLet
(orpush
pipe) - helper to check if we run zoneless or not
Before:
Bootstrap Time Before 425ms
Polyfills Before es5 132.32 KB, es2015 36.12 KB
After:
Bootstrap Time After 66ms
Polyfills After es5 126 B
Categories Add Modal
Styling
The categories component includes empty styles.scss file. This causes extra overhead in the build time and increases the bundle size.
- Solution:
Remove the styles file and import in the component
Optimize rendering, style recalculation and paint v1
To optimize style recalculation layout and paint we can apply the contain
styling rule
TL;DR
- Content section: from 513ms to 251 ms (262ms improvement)
- Sidebar section: from 53ms to 32 ms (21ms improvement)
- Toolbar section: marginal animation improvement
Total improvement 283ms
Movies section
When navigating to a movie list the whole document is rendered
<div _ngcontent-hub-movies-c24="" class="content">
<!-- movie list -->
</div>
the stylings for the content section are:
.content[_ngcontent-hub-movies-c24] {
min-height: calc(100vh - 72px);
width: 100%;
}
- 0 ms Scripting
- 415 ms Rendering
- 56 ms Painting
- 40 ms System
- 1 ms Idle
- 513 ms Total
To improve the render cost we can apply some styling rules.
.content[_ngcontent-hub-movies-c24] {
...
contain: strict;
}
This scopes the area that gets rerendered to the div.
- 1 ms Scripting
- 232 ms Rendering
- 11 ms Painting
- 8 ms System
- 0 ms Idle
- 251 ms Total
The overall improvement is minus 262ms ๐
Side drawer
The side drawer gets an update of fetched menu items. These causes rerender of the whole page.
Again we apply contain: strict;
.
<div class="side-drawer">
<!-- menu items -->
</div>
- 0 ms Scripting
- 3 ms System (self)
- 44 ms Rendering
- 6 ms Painting
- 0 ms Idle
- 53 ms Total
- 0 ms Scripting
- 25 ms Rendering
- 4 ms Painting
- 2 ms System
- 0 ms Idle
- 32 ms Total
The overall improvement is minus 31ms ๐
Toolbar and input animation
This is a small one but the approach is the same.
Here we apply contain: strict;
to the app-toolbar component:
<div _ngcontent-hub-movies-c24="" class="app-toolbar">
<!-- header toolbar -->
</div>
Measures After:
The impact is marginal but still fewer items processed.
Setup Lighthouse audits in CI
To have a way of measuring progress we need to set up continuous measures.
We use lighthouse-ci github action.
Todos:
- Cache build artefacts from Build & Test action
- setup lighthouse-ci action
- save artefacts
- serve panel
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.