Главная » Статьи » Контроль производительности веб-приложения с помощью JavaScript

Контроль производительности веб-приложения с помощью JavaScript

От автора: в сентябре у меня была возможность присоединиться к Forest Admin, компании, которая выполняет всю тяжелую работу по созданию панели администрирования любого веб-приложения и предоставляет платформу на основе API для реализации всех ваших конкретных бизнес-процессов.

Введение

Контроль производительности веб-приложения с помощью JavaScript

Моей первой миссией было реализовать мониторинг времени загрузки приложений, а также мониторинг времени запросов, выполненных клиентами в бэкэнде.

Контроль производительности веб-приложения с помощью JavaScript

Цель такой функции — иметь возможность ориентироваться на проекты, в которых некоторые пользователи сталкиваются с длительным временем загрузки, чтобы оптимизировать конфигурацию интерфейса. Это делает навигацию и взаимодействие с приложением более плавными и, таким образом, улучшает взаимодействие с пользователем.

Для этого первым делом нужно выяснить, как мы собираемся реализовать такую функцию. В зависимости от используемой среды разработки надстройки могут уже существовать. Если вы, например, разработчик React, response-addons-perf должен позволить вам отслеживать то, что вы хотите. В Forest Admin мы используем Ember.js, и надстройки, подобной React, у нас нет.

Поэтому мы будем использовать Performance API, который, согласно документации, должен позволять нам делать то, что мы хотим:

Стандарт High Resolution Time определяет интерфейс производительности, который поддерживает измерения задержки на стороне клиента в приложениях. Интерфейсы производительности считаются высокоразрешающими, поскольку они имеют точность до тысячных долей миллисекунды (в зависимости от аппаратных или программных ограничений).

Performance API

Performance API использует множество разнообразных методов. В нашем случае нам понадобится всего 5 разных методов:

mark

measure

getEntriesByType

clearMarks / clearMeasures

mark

Метод mark позволяет нам разместить временной маркер. Для ссылки на маркер требуется только один аргумент (строка), и он ничего не возвращает. Это позволит нам рассчитать время позже.

performance.mark('start');

measure

Метод measure позволяет измерить разницу во времени между двумя маркерами. Он принимает 3 аргумента: имя созданного измерения (строка), маркер начала (строка), маркер конца (строка). Этот метод возвращает объект со свойством длительности, которое вычисляет разницу между двумя маркерами.

async function timeDuration() { performance.mark('start'); await new Promise(resolve => setTimeout(resolve, 100)) performance.mark('end'); return performance.measure('time', 'start', 'end').duration;
} timeDuration().then((result) => console.log(result));
// output: 100

getEntriesByType

Метод getEntriesByType позволяет получить доступ ко всем созданным сущностям определенного типа. Он возвращает массив объектов и принимает в качестве аргумента тип ввода (строку) из следующего: фрейм, навигация, ресурс, метка, мера, рисование и длинная задача. Не волнуйтесь, нам понадобится только разметка и измерение.

performance.getEntriesByType('measure');
// output: return an Array of Object containing all the measure

clearMarks / clearMeasures

Методы clearMarks / clearMeasures используются для удаления ранее добавленных маркеров и показателей из кеша браузера. Эти методы ничего не возвращают и не принимают никаких аргументов.

performance.mark('start');
performance.mark('end');
performance.measure('time_duration', 'start', 'end'); console.log(performance.getEntriesByType('mark').length);
// output: 2
console.log(performance.getEntriesByType('measure').length);
// output: 1 performance.clearMarks();
performance.clearMeasures();
console.log(performance.getEntriesByType('mark').length);
// output: 0
console.log(performance.getEntriesByType('measure').length);
// output: 0

Давайте погрузимся в код

Теперь, когда мы знаем методы реализации функционала, нам необходимо интегрировать их в существующий код. У нас есть две возможности: создать маркеры и проводить измерения непосредственно в нужных местах в желаемом коде или создать сервис (time-tracker.js) и внедрить его в код. Для наглядности выберем второй вариант.

export default Service.extend({ clearMarks() { performance.clearMarks(); }, startInterface() { performance.mark('timing_interface_start'); }, stopInterface() { performance.mark('timing_interface_stop'); }, measureTimingInterface() { return performance.measure( 'timing_interface', 'timing_interface_start', 'timing_interface_stop' ); }, startRequest() { performance.mark('timing_request_start'); }, stopRequest() { performance.mark('timing_request_stop'); }, measureTimingRequest() { return performance.measure( 'timing_request', 'timing_request_start', 'timing_request_stop' ); },
});

Просто, не правда ли? Теперь нам нужно вызвать служебные функции в тех местах кода, которые позволят нам отслеживать время загрузки запроса и интерфейса.

Для интерфейса маркер time_interface_start должен вызываться в начале загрузки страницы. Метод модели маршрута вызывается первым, мы решили поставить его здесь. Также возможно использовать методы в жизненных циклах компонента или любой другой функции, вызванной изначально.

Однако следует соблюдать осторожность при размещении маркера. Если маркер не поставить в нужное место, последующее измерение не будет точным.

import { inject as service } from '@ember/service'; export default class RouteExample extends Route { @service timeTracker; model() { this.timeTracker.startInterface(); // do something }
}

Для маркера Timing_interface_stop он должен вызываться, когда рендеринг приложения только что закончился. Что касается рендеринга нескольких компонентов, логичным способом является использование методов в жизненных циклах этих компонентов. Метод компонента didRender ember кажется хорошим кандидатом.

Затем идет измерение общего времени загрузки интерфейса. Его можно разместить после маркера Timing_interface_stop или в любом другом желаемом месте.

import { inject as service } from '@ember/service'; export default class RenderingTracker extends Component { @service timeTracker; didRender() { this.timeTracker.stopInterface(); } stopInterfaceTracking() { return this.timeTracker.measureTimingInterface() }
}

Для расчета времени запроса на сервере пользователя достаточно окружить функцию, отвечающую за запрос сервера пользователя, маркерами Timing_request_start и Timing_request_stop. А затем мы можем измерить время запроса, вызвав метод measureTimingRequest() нашего сервиса.

import { inject as service } from '@ember/service'; export default class RouteExample extends Route { @service timeTracker; model() { this.timeTracker.startInterface(); // do something } async function fetchData(params) { //do something this.timeTracker.startRequest(); const records = await fetchRecords(params); this.timingTracker.stopRequest(); const timingRequest = this.timingTracker.measureTimingRequest() // do something else }
}

Заключение

Надеюсь, вам понравилась эта статья. Любые комментарии приветствуются, и я буду рад их обсудить. Я хотел бы воспользоваться этой возможностью, чтобы поблагодарить людей из Forest Admin, которые помогли мне реализовать эту функцию.

Есть и другие возможности реализовать этот функционал. Я решил показать вам путь, который имеет для меня больше смысла. API Performance — это мощный инструмент для получения информации о производительности вашего приложения. Важно знать, сталкиваются ли пользователи с трудностями при просмотре вашего приложения. Более того, это позволяет вам заранее определять проблемы, чтобы решать их и улучшать взаимодействие с пользователем.

Следующим шагом может быть интеграция последующего электронного письма для пользователей, время загрузки которых превышает пороговое значение, чтобы помочь им выбрать определенные параметры, которые необходимо оптимизировать.

Автор: Louis Clisson

Источник: medium.com

Редакция: Команда webformyself.