Максим Сальников
Angular GDE
Как создавать Angular-приложения, которые загружаются и работают быстро
@webmaxru
Full-stack разработчик "приложений из будущего" в ForgeRock
$ ng build --prod
$ ng build --prod --source-map
$ npx source-map-explorer main.bundle.js
const appRoutes: Routes = [{
path: 'tweets',
loadChildren: 'tweets/tweets.module#TweetsModule'
}]
const tweetsRoutes: Routes = [{
path: '',
component: TweetFeedsComponent
}];
@NgModule({
imports: [RouterModule.forRoot(appRoutes, {
preloadingStrategy: PreloadAllModules
})],
exports: [RouterModule]
})
$ ng add @angular/pwa
$ ng build --prod
Приложение
Браузер
Приложение
Service worker
Cache Storage
Браузер
{
"assetGroups": [
Уместные значения по умолчанию для Application Shell
],
"dataGroups": [
Настройки для динамического кеширования (API, CDN, ...)
],
...
}
{
"name": "myApi",
"urls": [
"/api/archive/**"
],
}
"cacheConfig": {
"strategy": "performance",
"maxSize": 100,
"maxAge": "365d"
}
OS
First meaningful paint
First meaningful paint
Без SSR
SSR
<app>Загружаем...</app>
Загружаем...
$ ng add @ng-toolkit/universal
$ npm run build:prod && npm run server
$ ng add @nguniversal/express-engine
$ npm run build:ssr && npm run serve:ssr
Change detection
Отрисовка UI
Запуск
Готово
<span>{{ relativeDate(tweet.createdAt) }}</span>
<span>{{ tweet.createdAt | relativeDate }}</span>
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'relativeDate',
pure: true
})
export class RelativeDatePipe implements PipeTransform {
transform(date) {
// Трансформации...
return newDate;
}
}
Cохранение результатов выполнения функций для предотвращения повторных вычислений
import { memoize } from 'lodash-decorators';
...
@memoize()
relativeDate(date) {
// Вычисления
return newDate;
}
По умолчанию
По умолчанию
По умолчанию
По умолчанию
import { ..., ChangeDetectionStrategy } from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
import { ..., ChangeDetectorRef } from '@angular/core';
class RealTimeList {
constructor(private ref: ChangeDetectorRef) {
ref.detach();
setInterval(() => {
this.ref.detectChanges();
}, 1000);
}
}
import { ..., NgZone } from '@angular/core';
class MyComponent implements OnChanges {
constructor(private _ngZone: NgZone ) {}
log() {
this._ngZone.runOutsideAngular(() => {
// Код здесь не вызовет CD
}}));
}
}
export class TweetListComponent implements OnInit {
...
trackById(index, tweet) {
return tweet.id;
}
}
<div *ngFor="let tweet of tweets; trackBy: trackById">