Суперсилы Chrome DevTools

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

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

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

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

Отладка Цена за скорость разработки — наличие багов. Этот класс задач требует быстрого реагирования, а значит и входа в контекст происходящего.Неверный результат на выходе Часто баг проявляется в каком-то некорректном внешнем событии: непредвиденное изменение DOM-дерева, асинхронный запрос с ошибочными данными и т.д. В этом случае удобно рассматривать код приложения как черный ящик, вход которого — сценарий использования, а выход — результат бага.Помимо стандартных брейкпоинтов на строчке кода, в DevTools (здесь и далее речь идет об инструментах браузера Google Chrome) есть возможность остановить выполнение по определенному событию.

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

500080fb6fe64b7992e2ed93b9217138.png

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

0d3e8b207c57400e9fab16be7cb1d1f5.png

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

ea81e2559dc64340956df3b38cabe981.png

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

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 компактные группы логов при изменении свойств объекта. Кода теперь намного больше, и каждый раз писать его по памяти не удобно. Поэтому можно сохранить его как сниппет и запускать по необходимости.

8847ff4eed614172aa367c7d50a95055.png

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

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

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:

ec80bc9194494c97bcf40a58672ed20f.png

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

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

b626bd981e2b4bc3819d1cc4a611abc8.png

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

8557106f945443adb16afd11cfe47959.png

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

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

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

© Habrahabr.ru