Pull to refresh

Chrome DevTools: Хитрости при отладке

Reading time7 min
Views53K

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

В этой заметке я бы хотел остановиться на различных нюансах, полезных при отладке. Какие-то из них я почерпнул в сети (например в комментариях на Хабре), до каких-то додумался сам. Надеюсь вы найдёте для себя что-нибудь полезное.

Все горячие клавиши в статье будут даны для linux/windows.

PopUps, popovers, dropdowns и прочие "всплывашки"

Начну с простого. Я думаю, многие сталкивались с ситуацией, когда у вас есть какая-нибудь "всплывашка", и вам нужно что-нибудь в ней поковырять. Но вот незадача - стоит убрать фокус из окна вкладки, как всплывашка тут же пропадает. Если код полностью под вашей властью, то можно просто отключить механизм сокрытия на onBlur. А если нет?

Вот простое решение (upd: вот решение получше от @kamre):

  • Открываем console

  • Запускаем: setTimeout('debugger;', 3_000)

  • Успеваем за эти три секунды активировать всплывашку

  • debugger тормозит вкладку

  • Всплывашка в вашем полном распоряжении

upd от @ChernovKirilo: в ряде окружений можно решить вопрос даже проще. Достаточно активировать режим "сделать скриншот области экрана". Перед тем как сделать скриншот весь экран перегораживает "подсветка" (overlay). Она может не вызвать onBlur, так что "всплывашка" не потухнет. Но при этом позволит вам сдвинуть курсор мыши за пределы страницы:

  • Активируем скриншот-утилиту (например. кнопка PrintSrcreen на Windows)

  • Появляется overlay

  • Передвигаем курсор за пределы страницы (например на панель devTools)

  • Отменяем скриншот (Esc?)

  • Всплывашка в вашем полном распоряжении

Бесконечный цикл

Иногда случается так, что в результате ошибки, ваш код бесконечно что-нибудь считает. И конца и краю этому нет. Например баг при работе с счётчиками для for(;;). Ну или какие-нибудь реактивные примитивы обновляют друг друга по кругу. Причин может быть много. Главное, что с этим делать? Думаю многие заметили, что даже попытка обновить страницу часто не помогает. Страница просто зависает, вкладка не закрывается, творится чёрт знает что. Да ещё и кулер бешено гудит.

Начнём с того - как же убить вкладку?

  • Запускаем Task Manager (Shift + Esc ). Его можно найти в "главном меню" - "More Tools" - "Task Manager".

  • Находим искомую вкладку в списке, выделяем.

  • Жмём кнопку "End Process". Вкладка убита.

Хорошо, а что если мы не хотим её убивать?

  • Открываем "Sources"

  • Кликаем на "Pause"

Теперь вы в процессе отладки JS. Самое время понять причину бага. Разобрались? Отлично. Что дальше? Вы хотите вернуть эту же вкладку к жизни, потому что на странице есть важные данные или условия для эксперимента? Но вам мешает этот чёртов вечный цикл?

Просто "сломайте" код. Во время отладки вы можете запускать произвольный JS код в консоли. При этом у вас есть доступ к локальным переменным того участка кода, который сейчас остановлен.

Вы можете попробовать запустить что-то вроде throw 'error', но скорее всего это вам ничего не даст, т.к. ваши команды в консоли обёрнуты в try-catch. Но можно сломать какой-нибудь объект. Предположим у вас там есть объект obj, и у него есть поле-метод field. Код использует его так: obj.field(). Прекрасно, пишем obj.field = null , запускаем JS, код падает, в консоли ошибка, вечный цикл побеждён. Понятное дело, что способов всё поломать тысячи, просто выберите нужный.

Деактивация breakpoint-а

Небольшой нюанс, о котором знают не все. Поэтому я решил добавить его в подборку. Дело в том что breakpoint-ы можно не только добавлять и удалять, но и деактивировать. Для этого у вас есть три способа:

  • Вот эти галочки справа:

  • Вот это контекстное меню:

  • Просто Shift + LeftClick по панели breakpoint-а:

Логирование при помощи breakpoint-ов

Тут всё просто. Не обязательно расставлять в кодовой базе console.log(), когда можно это сделать прямо в DevTools. Дело в том что у breakpoint-ов бывают условия:

Если условие задано, то breakpoint остановит исполнение JS только если его значение truthy. Напишите в условие console.log(whatever). Всё. Т.к. console.log всегда возвращает undefined то ваш breakpoint никогда не остановит JS, но зато всегда будет послушно логировать.

upd: Оказалось, что для логирования есть отдельная опция "Add logpoint":

По сути тоже самое, но без необходимости писать console.log(. Да и помечаются розовым цветом, а не синим или оранжевым. В графе breakpoints они тоже отображаются (т.е. это по сути сахар поверх обычных breakpoint-ов).

HotFix-ы посредством breakpoint-ов

Механизм тот же самый. Предположим вы отлаживаете один баг, но в процессе отладки обнаружили другой, который всё портит, и вы не хотите на него отвлекаться. А ещё вы не хотите перезагружать вкладку, т.к. эту ситуацию вы потом уже не воспроизведёте. Что делать? Можно в breakpoint-условии написать что-нибудь, что чинит второй баг.

Например, что-нибудь ломается из-за отсутствующего метода. Ок, напишите в условии (obj.nonExistingMethod = () => null) && null. Код временно "починился". && null нужен только для того, чтобы breakpoint не останавливал исполнение JS.

Таким образом можно добиться очень многого. Зависит уже от вашей фантазии и сложности отладки. Ключевое:

  • нет нужды перезагружать вкладку (это может быть бесценным).

  • breakpoint-условие будет послушно запускаться всякий раз когда JS-интерпретатор будет добираться до breakpoint-а. По сути это аналогично тому что вы добавили строку кода ровно туда, куда нужно, ничего по факту не добавляя.

Breakpoint посреди строки

Не все замечали, но Chrome DevTools позволяют выставить breakpoint не на всю строку, а в какую-нибудь её часть:

Это может сэкономить вам кучу времени.

Имена собственные

Если вы используете sourceMaps, то код, которые на самом деле исполняется, и тот который вы видите перед глазами могут очень сильно отличаться. В частности это касается имён локальных переменных. Пример:

Что будет если в консоли выполнить setState?

И как теперь это дебажить? А чёрт его знает. Но могу дать пару советов.

  • 1-й: поищите настоящие имена вот здесь:

upd от @extempl: не всегда новые наименования это одна\две буквы. К примеру для поддержки ES6 Import/Export webpack даёт вот такие длинные названия:

lodash_isObject__WEBPACK_IMPORTED_MODULE_0___default.

Чтобы упростить себе жизнь, можно нажать на ней (в панели Scope) RightMouseClick и выбрать "Store object as global variable". DevTools выдаст вам новую переменную вида temp1.

  • 2-й: отключите sourceMaps (это можно делать live, без перезагрузки страницы):

  • 3-й (от @mrShadow): в ряде случаев можно добраться до аргументов функции через arguments[index].

SourceMap очень часто усложняют отладку до уровня nightmare. У @vintage где-то (где кстати?) была хорошая статья по этому поводу. Там объясняется почему SourceMap такие плохие, когда дело касается отладки. Я сам то и дело включаю\выключаю эту галочку, в зависимости от обстоятельств.

Кстати говоря, если вас смущает поведение отладчика в казалось бы нормальном коде, посмотрите как он выглядит на самом деле. Там нередко случаются цепочки с оператором , , незапланированные ?: . Часть условий может быть перестроена. Проще говоря код может разительно отличаться от оригинала. Хорошо когда если есть возможность избежать минификации.

BreakPoint-ы и PrettyPrint

Минифицированный код можно отформатировать так, чтобы с ним можно было работать:

Но очень часто даже после этого с ним невозможно работать. BreakPoint-ы ставятся куда попало (куда не кликай он появляется либо вначале файла, либо в его конце). Отладка просто сходит с ума (к примеру всегда указывает на первую строку файла).

Что делать?

  • Открыть этот локальный файл в редакторе

  • Отформатировать код файла (например Prettier-ом)

  • Сохранить (да, даже если это сторонняя библиотека)

  • Убедиться что пересборка webpack\rollup\whatever учла это изменение

Теперь вам не нужен DevTools-ий pretty print. Можно debug-ить напрямую.

Перехват запросов

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

Находим нужный запрос в network-вкладке и наводим на него мышью:

Кликаем по 1-й (не обязательно) из ссылок, попадая в то место, где этот запрос был вызван. Ставим точку останова. Воспроизводим нужные действия в UI, чтобы этот запрос перевызвать.

В этом месте мы можем подправить параметры ещё до того как запрос ушёл на сервер. Отсюда же можно отследить то место, где будет обработан результат запроса:

Наверняка есть и более удобные способы перехватить запрос. Поделитесь вашим рецептом в комментариях!

Race Condition

Что делать, если у вас есть сложная многостадийная асинхронная логика и с ней что-то не так. Например у вас race condition. Как проще всего воссоздать условия для его обнаружения? Я думаю, тут есть множество рецептов. Здесь я остановлюсь только на одном из них. В network вкладке есть такой dropdown:

Наверняка многие из вас им уже пользовались. Offline режим позволяет отловить ошибки при проблемах с сетью. Slow 3G проследить как быстро грузится ваш сайт при медленном интернете. Но обратите также внимание на секцию "Custom". Это заданные вручную режимы. Просто нажмите "Add" (внизу списка), чтобы добавить свой.

Какие режимы использую я?

  • Never: максимальная задержка. Просто выставите огромное число. Например сутки. Теперь любой запрос повисает до тех пор, пока вы не выключите этот режим. Запросы не "умирают", а именно повисают. Это даёт вам неограниченное время для отладки какой-нибудь промежуточной стадии вашей зубодробительной логики.

  • 2\5\30sec latency: просто большая задержка. Актуальна когда вы отлаживаете "плавающий" баг. То он есть, то его нет. Или, скажем, когда вам нужно успеть сделать какое-нибудь быстрое действие мышью между двумя запросами. Или более пристально проследить что происходит, как бы в режиме slow-mo.

Disable cache

Думаю большинство из нас знакомы с этой галочкой:

Здесь я хотел бы отметить, что не стоит на постоянной основе ей пользоваться. Дело в том, что такое поведение браузера очень далеко от нормального. Браузер перестаёт пользоваться не только старым кешем, оставшимся от предыдущей страницы, но и новым. Если покажете картинку, удалите её, а потом снова создадите с тем же SRC, то у вас будет два запроса.

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

Помимо прочего это мешает работать любой логике где есть подготовительные стадии с прогревом кеша. Например когда перед показом изображения вы где-нибудь успеваете его загрузить посредством new Image().

В целом рекомендую поглядывать на запросы во вкладке network. Например можно случайно обнаружить, что отключён gzip/brotli. Или что CDN перестал присылать cache-control заголовок. Или что ещё вчера страница загружала 1 MiB, а сегодня 12 MiB (например какой-нибудь серверный endpoint стал присылать тонны JSON-а).

HI-DPI

Что делать, если у вас обычный монитор, а клиенты жалуются на мыло на мобильных устройствах\retina экране? Для начала активируйте нужный DPR режим:

Теперь браузер имитирует HI-DPI. И всё стало таким угловатым (нюансы сглаживания при resize). Но как проверить настоящую картинку?

  • Открываем палитру команд (Ctrl+P)

  • Ищем: capture area screnshoot

  • Выделяем мышью нужную область экрана, сохраняем в файл

  • Открываем файл и наслаждаемся огромной картинкой. Смотрим правда ли она "мыльная"?

Обычными системными скриншотами, разумеется, этого не достичь. Полагаю этот же рецепт работает и в обратную сторону (имитация 1х экрана).

Это всё?

Если вам понравился этот материал и вы хотите продолжения - дайте знать. У меня ещё много всего в черновиках. Если у вас есть свои трюки\рецепты\хаки - пишите их в комментариях. Думаю многие найдут их полезными и применят в деле.

Если вы нашли синтаксическую\стилистическую ошибку (или тонну таких ошибок) прошу обращаться в личку (а не в комментарии).

Only registered users can participate in poll. Log in, please.
Писать вторую часть?
96.33% Да236
0.82% Да, но… (уточню в комментариях)2
2.86% Нет7
245 users voted. 21 users abstained.
Tags:
Hubs:
Total votes 44: ↑43 and ↓1+48
Comments41

Articles