Мониторинг веб-приложений
Оригинал статьи: Observing your web app
В вебе есть множество API для информирования о том, что происходит в вашем приложении. Вы можете мониторить множество вещей и наблюдать практически любые типы изменений.
Эти изменения варьируются от простых вещей типа мутаций DOM и отлова ошибок на клиентской части до более сложных уведомлений типа разрядки батареи устройства пользователя. При этом остается постоянным набор способов взаимодействия с ними: функции обратного вызова, промисы, события.
Ниже показаны отдельные примеры их использования, которые я придумал. Этот список совсем не исчерпывающий. В основном это примеры мониторинга структуры приложения, его состояния и свойств устройства, на котором оно выполняется.
Прослушиваем события DOM (как нативные, так и кастомные):
// user scrolls the page.
window.addEventListener('scroll', e => { ... });
el.addEventListener('focus', e => { ... }); // el is focused. img.addEventListener('load', e => { ... }); // img is done loading.
// user types into input.
input.addEventListener('input', e => { ... });
// catch custom event fired on el.
el.addEventListener('custom-event', e => { ... });
Прослушиваем модификацию DOM:
const observer = new MutationObserver(mutations => { ... }); observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
characterData: true
});
Узнаем об изменении URL:
window.onhashchange = e => console.log(location.hash); window.onpopstate = e => console.log(document.location, e.state);
Узнаем о работе приложения в полноэкранном режиме (подробнее):
document.addEventListener('fullscreenchange', e => {
console.log(document.fullscreenElement)
});
Узнаем о поступлении сообщений по WebRTC:
// Cross-domain / window /worker.
window.onmessage = e => { ... };
// WebRTC
const dc = (new RTCPeerConnection()).createDataChannel(); dc.onmessage = e => { ... };
Узнаем об ошибках на клиентской стороне (подробнее):
// Client-size error?
window.onerror = (msg, src, lineno, colno, error) => { ... };
// Unhandled rejected Promise?
window.onunhandledrejection = e => console.log(e.reason);
Отслеживаем изменение ориентации экрана устройства (подробнее):
const media = window.matchMedia('(orientation: portrait)'); media.addListener(mql => console.log(mql.matches));
// Orientation of device changes.
window.addEventListener('orientationchange', e => {
console.log(screen.orientation.angle)
});
Отслеживаем изменения в сетевом соединении (подробнее):
// Online/offline events.
window.addEventListener('online', e => {
console.assert(navigator.onLine)
});
window.addEventListener('offline', e => {
console.assert(!navigator.onLine)
});
// Network Information API navigator.connection.addEventListener('change', e => {
console.log(navigator.connection.type,
navigator.connection.downlinkMax);
});
Отслеживаем состояние заряда батареи устройства (подробнее):
navigator.getBattery().then(batt => {
batt.addEventListener('chargingchange', e => {
console.log(batt.charging);
});
batt.addEventListener('levelchange',e => {
console.log(batt.level);
});
batt.addEventListener('chargingtimechange', e => {
console.log(batt.chargingTime);
});
batt.addEventListener('dischargingtimechange', e => {
console.log(batt.dischargingTime);
});
});
Узнаем о видимости/нахождении в фокусе вкладки или страницы (подробнее):
document.addEventListener('visibilitychange', e => {
console.log(document.hidden)
});
Узнаем о смене пользователем расположения:
navigator.geolocation.watchPosition(pos => console.log(pos.coords));
Узнаем о изменении разрешений на использование нужного API (подробнее):
const q = navigator.permissions.query({name: 'geolocation'}); q.then(permission => {
permission.addEventListener('change', e => {
console.log(e.target.state);
});
});
Узнаем об обновлении другой вкладкой локального хранилища или хранилища сессии:
window.addEventListener('storage', e => alert(e));
Узнаем о появлении или покидании элементом области видимости (то есть о видимости элемента, подробнее):
const observer = new IntersectionObserver(changes => {
...
}, {threshold: [0.25]}); observer.observe(document.querySelector('#watchMe'));
Узнаем, когда браузер находится в “ленивом” режиме (и готов выполнить какую-нибудь дополнительную работу, подробнее):
requestIdleCallback(deadline => { ... }, {timeout: 2000});
Узнаем о загрузке браузером ресурса или о записи метрики User Timing (подробнее):
const observer = new PerformanceObserver(list => {
console.log(list.getEntries());
});
observer.observe({entryTypes: ['resource', 'mark', 'measure']});
Узнаем об изменении свойств объекта (включая свойства DOM, подробнее):
// Observe changes to a DOM node's .textContent.
// From gist.github.com/ebidel/d923001dd7244dbd3fe0d5116050d227
const proxy = new Proxy(document.querySelector('#target'), {
set(target, propKey, value, receiver) {
if (propKey === 'textContent') {
console.log('textContent changed to: ' + value);
}
target[propKey] = value;
}
});
proxy.textContent = 'Updated content!';
Круто! И, главное, что еще большее количество API находится в разработке.
Я предполагаю, что вы могли бы классифицировать некоторые из этих примеров как техники и паттерны (например, реагирование на события DOM). Однако многие совершенно новые API создавались с определенной целью: измерение производительности, информирование о заряде батареи, состояния онлайн или офлайн.
Это действительно впечатляет, к каким вещам у веб-разработчиков есть доступ в наши дни. API есть практически для всего.