От автора: в сентябре у меня была возможность присоединиться к Forest Admin, компании, которая выполняет всю тяжелую работу по созданию панели администрирования любого веб-приложения и предоставляет платформу на основе API для реализации всех ваших конкретных бизнес-процессов.
Введение
Моей первой миссией было реализовать мониторинг времени загрузки приложений, а также мониторинг времени запросов, выполненных клиентами в бэкэнде.
Цель такой функции — иметь возможность ориентироваться на проекты, в которых некоторые пользователи сталкиваются с длительным временем загрузки, чтобы оптимизировать конфигурацию интерфейса. Это делает навигацию и взаимодействие с приложением более плавными и, таким образом, улучшает взаимодействие с пользователем.
Для этого первым делом нужно выяснить, как мы собираемся реализовать такую функцию. В зависимости от используемой среды разработки надстройки могут уже существовать. Если вы, например, разработчик 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.