Технопарк, осень, 2025 г.
<!-- резолвит DNS заранее --><meta http-equiv="x-dns-prefetch-control" content="on"><link rel="dns-prefetch" href="//api.myawesomegame.io"><!-- dns-prefetch на стероидах, также инициирует коннект --><link rel="preconnect" href="//api.myawesomegame.io"><!-- загружает ресурс с низким приоритетом и кладет в кеш --><link rel="prefetch" href="https://myawesomegame.io/img/sprite.jpg"><!-- загружает ресурс с высоким приоритетом и кладет в кеш --><link rel="subresource" href="https://myawesomegame.io/img/sprite.jpg"><!-- загружает страницу со всем содержимым в фоне, строит DOM --><link rel="prerender" href="https://myawesomegame.io/about.html">
const img = new Image();img.src = 'https://myawesomegame.io/img/sprite.jpg';
window.performance.timing
// Покажет всю сетевую активностьwindow.performance.getEntries()// Отобразит все загружаемые ресурсыperformance.getEntriesByType('resource')
document.addEventListener("DOMContentLoaded", () => {// DOM дерево построено, но загружены не все ресурсы});
document.addEventListener("load", () => {// Все ресурсы загружены});
Плюсы:// В запросеAccept-Encoding: gzip, deflate, br// В ответеContent-Encoding: gzip
Необходимо включать отдачу сжатой статики на сервере# Заранее сжимаем статику./zopfli -c app.js > app.js.gz
# пример для nginxgzip_static on;
# пример для nginxbrotli on;
| WebP (4,9 KB) | GIF (85,2 KB) |
|
|
<img src="image.webp" onerror="this.onerror=null; this.src='image.png'">
<picture><source srcset="image.webp" type="image/webp"><source srcset="image.jpg" type="image/jpeg"><img src="image.jpg"></picture>
Компилируем в jssyntax = "proto3";message User {string name = 1;int32 age = 2;}
Можно использовать webpack-loader 😏npm install -g protobufjspbjs -t static-module -w commonjs -o user.js user.proto
Кэширование — повторное использование ресурсов для работы приложения. Использование кэшей позволяет уменьшить задержку и расходование сетевого трафика и тем самым уменьшить время, необходимое для отображения ресурса. HTTP-кэширование — кэширование ответов на HTTP-запросы
// Запрос обыкновенныйGET /script.js HTTP/1.1Host: example.comAccept: */*
// Ответ на запросHTTP/1.1 200 OKContent-Type: application/javascript; charset=UTF-8Cache-Control: public, max-age=86400Last-Modified: Sat, 25 Mar 2017 12:00:00 GMTETag: W/"7349b-15b075b6d60"
Cache-ControlВозможные значения
no-cacheno-storemust-revalidateprivate/publicmax-age=31536000// Ответ на запросHTTP/1.1 200 OK...Last-Modified: Sat, 25 Mar 2017 12:00:00 GMT...
GET /script.js HTTP/1.1Host: example.comIf-Modified-Since: Sat, 25 Mar 2017 12:00:00 GMT
HTTP/1.1 304 Not Modified // Контент не изменилсяHTTP/1.1 200 OK // Новый контент
// Ответ на запросHTTP/1.1 200 OK...ETag: W/"7349b-15b075b6d60"...
GET /script.js HTTP/1.1Host: example.comIf-None-Match: W/"7349b-15b075b6d60"
HTTP/1.1 304 Not Modified // Контент не изменилсяHTTP/1.1 200 OK // Новый контент
GET /script.js?_=1492172324776GET /bundle.026f8e459c8f89ef75fa7a78265a0025.jsВсе данные, которые используются web-приложением, существуют только пока открыта вкладка браузера. Однако, существуют способы сохранить какие-то данные в браузере и воспользоваться ими потом:
Web Storage API — механизм для сохранения key/value значений с возможностью программного управления данными. Предоставляет два host объекта в браузере пользователя с возможностью персистентного сохранения данных (до 10 MB на origin)
window.sessionStorage — сохраняет данные пока открыт браузерwindow.localStorage — сохраняет данные навсегда, пока пользователь вручную не очистит
хранилище данных в настройках браузера
window.localStorage// элементами Storage являются строкиlocalStorage[key]; /* String */localStorage[key] = value; /* String */// работа с объектами Storage синхроннаяlocalStorage.lengthlocalStorage.key(i) /* String */localStorage.getItem(key) /* String */localStorage.setItem(key, value) // может сгенерировать exception,// если нет местаlocalStorage.removeItem(key)localStorage.clear()
Можно написать обёртку, которая позволит сохранять в Storage простые объекты:
function setJSON(key, value) {localStorage[key] = JSON.stringify(value);}function getJSON(key) {const value = localStorage[key];return value ? JSON.parse(value) : null;}
storageСобытие storage происходит при любых изменениях в Storage в других вкладках с того же
origin. То есть это событие позволяет общаться между вкладками
// обработчик добавляется на объект windowwindow.addEventListener('storage', function (e) {/* e.key, e.newValue */...});
WebSQL — полноценная SQL база данных, которая позволяет персистентно хранить данные в браузере пользователя и работать с ними посредством SQL-запросов. Максимальный размер сохраняемых данных — 5 MB. Поддержка на caniuse.
// создаём объект базы данных (доступно и в воркерах!)const db = openDatabase('forum', 'v1.0.0', 'Forum', 100000);// создаём транзакциюdb.transaction(function(tx) {tx.executeSql('SELECT COUNT(*) FROM `forum`',[],function (result) { console.log(result) },function (tx, error) { /* some error logic */ });});
IndexedDB — низкоуровневое API для клиентского хранилища большого объема структурированных данных, включая файлы/blobs. Эти API используют индексы для обеспечения высоко-производительного поиска данных. Максимальный размер сохраняемых данных — 50 MB!!! Поддержка на caniuse.
// открываем базу данных Forum (доступно и в воркерах!)const request = window.indexedDB.open('Forum', 3); // 3 - версия бд// обработчик успешного открытия базы данныхrequest.onsuccess = function(event) {const db = event.target.result;const store = db.createObjectStore('users', { keyPath: 'userId' });store.createIndex('age', 'age', { unique: false });store.createIndex('email', 'email', { unique: true });store.add({ age: 21, email: 'a.ostapenko@corp.mail.ru' });};
С помощью FileSystem API и File API веб приложение может создавать, читать, просматривать и записывать файлы находящиеся в области пользовательской «песочницы». Крутой туториал. Поддержка на caniuse.
Протокол WebSocket — протокол полнодуплексной связи (может передавать и принимать одновременно) поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени. С помощью его API вы можете отправить сообщение на сервер и получить ответ без выполнения отдельного HTTP-запроса, причем этот процесс будет событийно-управляемым
Были созданы, чтобы обойти ограничение HTTP на формат запрос/ответ и дать возможность отправлять сообщения с сервера на клиент
Подробнее — по ссылке на learn.javascript.ru
Именно поэтому WebSocket'ы очень удобно использовать для написания:
WebSocketconst ws = new WebSocket('ws://example.com/ws');// если страница загружена по https://const ws = new WebSocket('wss://example.com/ws');// События WebSocketws.addEventListener('open', listener); // соединение установленоws.addEventListener('message', listener); // пришло новое сообщениеws.addEventListener('error', listener); // ошибкаws.addEventListener('close', listener); // сокет закрылся
После создания объекта WebSocket необходимо дождаться, пока соединение не откроется и не установится:
ws.onopen = function() {console.log('Соединение установлено, можно отправлять сообщения!');// Отправка текстаws.send('Hello!');ws.send(JSON.stringify({ x: 100, y: 150 }));// Отправка бинарных данных (например файлы из формы)ws.send(form.elements[0].file);};
error и closews.onerror = function(error) {// произошла ошибка в отправке/приёме данных или сетевая ошибкаconsole.log('Ошибка ' + error.message);};ws.onclose = function(event) {// 1000 - штатное закрытие сокета (коды WebSocket из 4х цифр)// 1001 - удалённая сторона исчезла// 1002 - ошибка протокола// 1003 - неверный запросconsole.log('Код: ' + event.code);console.log('Причина: ' + event.reason);};
message — обработка ws.onmessage = function(event) {const data = event.data;const message = JSON.parse(data);console.log('Прислали сообщение: ' + message.text);// или, если есть глобальная шина событийbus.emit(message.event, message.payload);};
var buffer = new ArrayBuffer(128);socket.send(buffer);var intview = new Uint32Array(buffer);socket.send(intview);var blob = new Blob([buffer]);socket.send(blob);
{"action": "FIRE","payload": { "cell": "b4" }}{"action": "FIRE_RESULT","payload": { "state": "Убил" }}
const webSocketService = new WebSocketService('/ws');webSocketService.send('FIRE', { "cell": "b4" });webSocketService.subscribe('FIRE_RESULT', function (payload) {const state = payload.state;game.reRender(state);});
Service Workers — продвинутая технология, которая позволяет получить полный контроль над жизненным циклом приложения. Сервис воркер — это воркер, который:
- работает в выделенном контексте и отдельном потоке
- имеет доступ к Cache Storage (расширенное хранилище данных)
- имеет возможность перехватывать HTTP-запросы, отправляемые страницей
- а так же может работать даже если само web-приложение или даже браузер не запущены
Подробнее — по ссылке на MDN
Работает в специальном скоупе ServiceWorkerGlobalScope, который не имеет
доступа к обычному скоупу с window
Имеет несколько событий, на которые можно навешивать
обработчики:
this.addEventListener('install', listener); // SW зарегистрировалиthis.addEventListener('activate', listener); // SW запустилиthis.addEventListener('fetch', listener); // SW перехватил запросthis.addEventListener('message', listener); // SW получил сообщениеthis.addEventListener('push', listener); // SW получил push
navigator.serviceWorker.register('/sw.js', { scope: '/' }).then(function(registration) {// Registration was successfulconsole.log('SW registration OK:', registration);}).catch(function(err) {// registration failed :(console.log('SW registration FAIL:', err);});});
this.addEventListener('install', function (event) {console.log('Service worker установлен')event.waitUntil(// находим Cache-объект с нашим именемcaches.open('MY_CACHE').then(function (cache) {// загружаем в наш cache необходимые файлыreturn cache.addAll(['/index.html']);}););});
HTTP/1.1 был спроектирован для сетей с более низкими пропускными способностями (bandwidth) и более высокими задержками (latency), чем сейчас. Поэтому у него есть недостатки:
HTTP/2 создавался с целью улучшить скорость работы web-приложений, за счёт уменьшения сетевых задержек и более удобного управления ресурсами в web. Основные особенности:
Сервера, которые поддерживают HTTP/2:
Поддержка браузерами — caniuse
Stream — двунаправленный поток байтов через установленное соединение, который может состоять из одного или более сообщений
Message — целостная последовательность фреймов, которая составляет полное логическое сообщение: запрос или ответ
Frame — минимальная единица коммуникации в HTTP/2. Каждый фрейм содержит заголовок фрейма, который идентифицирует, к какому сообщению внутри стрима относится это фрейм
404 Slide Not Found
История, больше касающаяся серверсайда/реверс-прокси и прочих сисадминских штучек. Используется для более тщательного контроля над пересылкой данных, буферизацией и всяким таким добром
HTTP/2 — абстракция
TCP — стрим из пакетов
packet loss — resend — delay
While it’s true that some of the HTTP/2 features can be mapped on top of QUIC very easily, that’s not true for all of them.
One in particular, HTTP/2’s header compression scheme called HPACK, heavily depends on the order in which different HTTP requests and responses are delivered to the endpoints.
QUIC enforces delivery order of bytes within single streams, but does not guarantee ordering among different streams.
* QUIC -> QPACK