Pull to refresh
91.39
2ГИС
Главные по городской навигации

Суперсилы Chrome DevTools

Reading time 6 min
Views 70K


Я работаю в команде Онлайн. Мы делаем веб-версию справочника 2ГИС. Это долгоживущий активно развивающийся проект, в котором JavaScript используется как основной язык как на клиенте, так и на сервере.

Важное место в работе занимают инструменты анализа и отладки приложения. Популярные JavaScript фреймворки как правило обладают собственным инструментарием, заточенным под конкретную идеологию. Наша ситуация осложняется тем, что под капотом Онлайна гудит фреймворк собственного производства — Slot — также находящийся в стадии активной доработки.

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

Проблема


Динамика разработки не позволяет участникам команды полностью погрузиться в детали задач друг друга. Контекст конкретного компонента быстро ускользает; вернувшись к участку кода спустя месяц, можно его не узнать. Кроме того, команда постоянно пополняется новобранцами.

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

Отладка


Цена за скорость разработки — наличие багов. Этот класс задач требует быстрого реагирования, а значит и входа в контекст происходящего.

Неверный результат на выходе


Часто баг проявляется в каком-то некорректном внешнем событии: непредвиденное изменение DOM-дерева, асинхронный запрос с ошибочными данными и т.д. В этом случае удобно рассматривать код приложения как черный ящик, вход которого — сценарий использования, а выход — результат бага.

Помимо стандартных брейкпоинтов на строчке кода, в DevTools (здесь и далее речь идет об инструментах браузера Google Chrome) есть возможность остановить выполнение по определенному событию.

DOM Breakpoint устанавливается на узел дерева в инспекторе. Остановиться можно при удалении этого элемента, изменении его поддерева или атрибутов (напомню, что style и class — это тоже атрибуты).



XHR Breakpoint устанавливается на весь документ и позволяет найти строчку кода, из которой посылается подпадающий под заданный паттерн запрос.



Эти брэйкпоинты отлично работают в связке с асинхронным режимом стэка вызовов (Async сall stack). Он не обрывается на асинхронных операциях и дает возможность, например, перейти из обработчика setTimeout к коду, который его установил. Таким образом, можно заглянуть намного дальше в историю и найти корни даже сложных багов.



Пример сценария:

1. Происходит непредвиденное изменение в DOM-дереве.
2. Поведение воспроизводится.
3. Устанавливаем нужный тип DOM-брэйкпоинта.
4. Включаем Async режим в отладчике.
5. Воспроизводим баг и путешествуем по истории вызовов, пока не найдем корни проблемы.

Неправильное внутреннее представление


Не все баги заметны невооруженным глазом. В ходе выполнения может меняться только внутреннее состояние, которое уже позже повлияет на поведение системы в целом. Отлавливать некорректные изменения этого состояния можно, используя воображение.

Предположим, что состояние — это глобальный объект. Тогда для слежки за ним можно использовать следующий код:
var watchMe = {};

Object.observe(watchMe, function() {
    debugger;
});

watchMe.foo = ‘bar’; // Выполнение остановится


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

var watchMe = {};

Object.observe(watchMe, function(options) {
    options.forEach(function(option) {
        var groupName = option.name + ' changed';
        console.groupCollapsed(groupName);
        console.log('Old value: ', option.oldValue);
        console.log('New value: ', option.object[option.name]);
        console.groupEnd(groupName);    
    });
});


Этот пример будет выводить консоль Chrome компактные группы логов при изменении свойств объекта. Кода теперь намного больше, и каждый раз писать его по памяти не удобно. Поэтому можно сохранить его как сниппет и запускать по необходимости.



Конечно, этот код придется каждый раз адаптировать к проекту и задаче. Редко все состояние хранится в одном глобальном объекте. Иногда придется редактировать исходники чтобы вклиниться в контекст выполнения. Но польза от такого подхода стоит прилагаемых усилий.

Сценарий использования:

1. Если наблюдаемый объект глобальный, то просто запускаем на нем сниппет.
2. Если объект доступен только в локальной области видимости, добавляем нужный код в приложение на время отладки.

Исследование


Работа программиста не ограничивается исправлением багов. Для добавления новой функциональности важно понимание работы приложения в целом на сложных сценариях.

Console как источник знаний


Консоль в DevTools — это не только способ быстро выполнить небольшой скрипт. Она обладает мощным API, реализующим недоступные в языке удобные функции и связки с другими инструментами DevTools.

Например, чтобы вывести в консоль DOM-элемент, не обязательно использовать сложные селекторы. Вместо этого в рамках консоли реализован стэк выделенных в инспекторе элементов. Доступ к нему происходит через команду $N, где N — отступ от конца стэка. Таким образом, обращение к $0 в консоли вернет последний выбранный в инспекторе DOM-узел, $1 — предпоследний и так далее.

Использовать эту возможность удобно в связке с функциями над DOM-узлами:

— monitorEvents(object) — следит за событиями элемента;
— getEventListeners(object) — выводит в консоль список обработчиков событий элемента, из которого можно перейти к коду функции.

Вырисовывается простой сценарий:

1. Выделяем элементы в инспекторе.
2. Вызываем в консоли нужную команду, аргументом передаем $0.
3. Радуемся, как дети.

Многие разработчики удаляют из кода console.log() сразу по окончании отладки. Но некоторая ключевая функциональность требует постоянного логирования. В результате каждый разработчик сначала пишет отладочный код, а потом его удаляет.

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

// Конфиг UglifyJS
global_defs: {
    DEBUG: false
}

// Где-то в коде приложения
if (DEBUG) {
    console.log(‘Что-то важное’);
}


В примере выше DEBUG не просто будет равен false после сборки. UglifyJS поймет, что условие никогда не выполнится, и просто удалит его из кода.

Теперь можно использовать более сложные возможности вывода в консоль для разных типов данных. В примере с отладкой состояния вывод будет более компактным за счет console.groupCollapsed. Это используется в нашем проекте для отслеживания изменения URL:



Для массивов хорошо подходит console.table. Полный список вариантов оформления вывода находится здесь.

С высоты птичьего полета


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

Одна из долгожданных фич — отображение стэков вызовов прямо в диаграмме событий. По сути это объединяет классический Timeline с отчетом CPU Profiler. Такая диаграмма дает понимание, как приложение работает в динамике: последовательности вызовов, общее время выполнения функций, точки взаимодействия JavaScript с DOM. Даже когда необходимости в оптимизации нет, его можно использовать для изучения внутреннего устройства проекта.



Если интересен конкретный участок логики приложения, а событий в отчете слишком много, можно воспользоваться console.time. Помимо замера времени выполнения этот метод добавляет метку в Timeline.



Пример использования:

1. Записываем пользовательский сценарий.
2. Изучаем стэки вызовов.
3. По необходимости переходим к конкретной строчке кода прямо из Timeline.
4. Если информации слишком много, оборачиваем интересующий код в console.time и console.timeEnd.
5. Повторяем до полного понимания логики происходящего.

Заключение


При работе над большими проектами имеет смысл инвестировать время в удобство разработки. Часы, потраченные на изучение и адаптацию инструментов, экономят дни отладки и дают лучшее понимание работы проекта.

В этой статье описана только малая часть возможностей Chrome Developer Tools. В работе над 2ГИС Онлайн у нас возникают и более экзотические задачи, требующие постоянного изучения новых средств разработки. Более подробно об этих инструментах, а также о производительности приложений на JavaScript, я рассказывал на конференции FrontTalks.
Tags:
Hubs:
+73
Comments 13
Comments Comments 13

Articles

Information

Website
2gis.ru
Registered
Founded
Employees
1,001–5,000 employees
Location
Россия