Is this a fast, convenient and responsive site? Perhaps this is not the result of the fruitful work of many people, but just a set of psychological and engineering tricks aimed at improving Perceived Performance.
In most cases, as real performance increases, so does Perceived Performance. And when the actual performance cannot be easily increased, there is an opportunity to increase the apparent performance. In his speech at Frontend Live 2020, former Avito Frontend Architecture developer Alexey Okhrimenko spoke about techniques that improve the feeling of speed where it is no longer possible to accelerate.
Performance is a must for the success of modern Web applications. There are a lot of studies from Amazon and Google that say that performance degradation is directly proportional to the loss of money and leads to poor satisfaction with your services (which again makes you lose money).
The root cause of the stress received is anticipation. It has been scientifically proven to cause:
anxiety;
uncertainty;
the discomfort;
;
.
- , . .
— . , : «, !», , .
: .
, :
lighthouse;
devTools profiler.
, .
Houston. .
: , , . , - .
. Huston , , 8 . .
, . . , . , . . , : 8 .
, ?
Perceived Performance
Perceived Performance, . . .
, , — .
, , . , , .
, . .
Angular , , , . !
: . : « !».
:
, - , , .
? ?
— ! .
?
«» (item ), . , , , , — — . : « 1 !» — . RxJs concat :
import { concat } from 'rxjs';
const source = concat(
myService.saveStuff({ test: 'swag '}),
myService.saveStuff({ test: 'yolo '})
);
, , , .
: « , - . , ».
, , :
<ngx-spinner [fullScreen]="false">
<p class="loading">Loading Awesomeness...</p>
</ngx-spinner>
Angular ngx-spinner, .
, , , -.
.
, . , , . .
: , Motion Blur ( ). . , , , , .
""?
Progress Bar;
, , , Progress Bar, . 12% , . , Progress Bar, 12% . , CSS.
;
— , ? , , .
— :
, .
, , 10 20%.
, .
Angular, React, View. , Angular ngx-skeleton-loader, . :
<ngx-skeleton-loader
appearance="circle"
[theme]="{ width: '80px', height: '80px' }"
>
</ngx-skeleton-loader>
CS:GO? ! , . Latency/Lag compensation.
frontend Optimistic Updates. Twitter
, backend 2 .
Optimistic Updates ? http :
addItem(item) {
return this.http.post<Item>('/add-item', item)
.subscribe(res => {
this.items.push(res);
}, error => {
this.showErrorMessage(error);
})
}
Optimistic Update:
addItem(item) {
this.items.push(item); //
return this.http.post<Item>('/add-item', item)
.subscribe(
res => {
//
}, error => {
// -
this.items.splice(this.items.indexOf(item), 1);
this.showErrorMessage(error);
}
);
}
State Managers , Optimistic Updates. , State Manager (redux, mobx, appollo-graphql, etc.)
, — , . Optimistic Updates : . . Optimistic Update:
addItem(item) {
return this.http.post<Item>('/add-item', item)
.subscribe(res => {
this.items.push(res);
}, error => {
this.showErrorMessage(error);
})
}
, API . , .
addItem(item) {
this.items.push(item); //
return this.http.post<Item>('/add-item', item)
.pipe(retry()) //
.subscribe(
// ...
);
}
best practice -, . , . : . API 100% , 99,9% uptime.
. , - . . , 3 .
addItem(item) {
this.items.push(item); //
return this.http.post<Item>('/add-item', item)
.pipe(
retry(3)
)
.subscribe(
// ...
);
}
DDOS (Distributed Denial of Service). . , Apple MobileMe.
? , , . , - 500. , , .
, , , DDOS. , - .
Best practice: exponential backoff. rxjs npm backoff-rxjs, .
import { retryBackoff } from 'backoff-rxjs';
addItem(item) {
this.items.push(item);
return this.http.post<Item>('/add-item', item)
.pipe(
retryBackoff({
initialInterval: 100,
maxRetries: 3,
resetOnSuccess: true
})
)
.subscribe(
// ...
);
}
, 10 . . , , , . : 1 , 2 , 4 ..
, API.
— Math.random() initialInterval:
import { retryBackoff } from 'backoff-rxjs';
addItem(item) {
this.items.push(item);
return this.http.post<Item>('/add-item', item)
.pipe(
retryBackoff({
initialInterval: Math.random() * 100,
maxRetries: 3,
resetOnSuccess: true
})
)
.subscribe(
// ...
);
}
, , , , . , .
!
, ?
. , , , , . -.
;
— . , SPA-. , . preload , :
var images = [];
function preload() {
for (var i = 0; i < arguments.length; i++) {
images[i] = new Image();
images[i].src = preload.arguments[i];
}
}
preload(
"http://domain.tld/image-001.jpg",
"http://domain.tld/image-002.jpg",
"http://domain.tld/image-003.jpg"
)
«», .
18+
HTML link, stylesheets:
<link
rel="stylesheet"
href="styles/main.css"
>
, :
<link
rel="preload"
href="styles/main.css"
as="style"
>
rel ="preload", (href="styles/main.css"), as .
prefetch.
— prefetch:
<link
rel="prefetch"
href="styles/main.css"
>
— , preload prefetch — . preload prefetch , preload , . , , , hero images ( ).
, , .
- JavaScript , 3 . , , — 1,2 . , .
?
Machine Learning
Guess.js. Google Google Analytics.
, , prefetch 7% .
90%. 7%, 90% . prefetching/preloading, , . Guess.js — .
Guess.js Angular, Nuxt js, Next.js Gatsby. .
Click-
, ?
<div (lick)="onClick()">
button! ;)
</div>
, ? .
, mousedown. 100-200 , Click.
:
<div (onmousedown)="onClick()">
button! ;)
</div>
click mousedown, 100-200 .
( RouR ), mousedown , , click — «» ( , )
mousedown hover - UX Accessibility.
, , mousedown hover :)
, , 240-500 , o_O.
ML? . : - , ( ).
, , , .
futurelink. :
var futurelink = require('futurelink');
futurelink({
links: document.querySelectorAll('a'),
future: function (link) {
// `link` is probably going to be clicked soon
// Preload everything you can!
}
});
DOM , . , callback. .
? : HTML, CSS .
SSR.
Angular :
ng add @nguniversal/express-engine
, Server-Side Rendering.
, Angular? , , ?
prerender: , HTML. webpack, PrerenderSPAPlugin:
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
module.exports = {
plugins: [
...
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: [ '/', '/about', '/some/deep/nested/route' ],
})
]
}
, urls, , .
: SPA :
document.documentElement.outerHTML
HTML : . . , ( ).
Perceived Performance — , . FrontendConf 2019.
, , 68% , . , , Perceived Performance . .
..
, . , . , . , . UX- .
, , Perceived Performance.
Perceived performance — . , , .