В сети полно обзоров 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х экрана).

Это всё?

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

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

Комментарии (41)


  1. korsetlr473
    03.11.2021 21:01
    +1

    1) в 2021 эта штука научилась отобржать raw request response ?

    2) в 2021 эта штука научилась перехватывать входящий респонс , заменять в нем данные и продолжать?


    1. faiwer Автор
      03.11.2021 21:28

      1. Ну тут как сказать. Если это binary, то оно пишет "this request has no response data available". Если это что-то строковое, то умеет. Но всё равно — заголовки и тело ответа отдельно. Можно скопировать "response". Для blob оно копирует data-uri, для условного JSON-а body-часть ответа. Заголовки показывает в табличном виде. После ряда фильтров (когда дебажил это всё дело Wireshark-ом видел что реальные заголовки HTTP2 несколько отличаются. В общем если нужен простой ответ, то нет.
      2. Не видел такой возможности. Не уверен что такое есть даже в Roadmap


    1. alaudo
      04.11.2021 11:24

      Для этого можно и нужно использовать Fiddler, он во все это умеет.


      1. faiwer Автор
        04.11.2021 11:31
        +2

        screenshot

        image


        Он правда стоит своих 10+ USD в месяц?


        1. mayorovp
          04.11.2021 11:40

          Ёпрст, когда он успел стать платным?!


        1. jodaka
          04.11.2021 19:08

          есть кроссплатформенный Charles, который у меня за 7 лет использования отбил свою стоимость 25 раз


      1. korsetlr473
        04.11.2021 21:42

        установил наш фидлер , да 1й пункт RAW req - res идеально отображает.

        второй пункт не нашел , как называется или как сделать куда жать?


        1. mayorovp
          04.11.2021 22:20

          Auto responder вроде бы называлась фича


    1. Tatikoma
      05.11.2021 17:53
      +1

      Есть замечательный mitmproxy, который это умеет. Кроме того сам DevTools Protocol поддерживает перехват запросов, в т.ч. можно не отправлять запрос на сервер, а выдавать сразу подменённый ответ (только вот в интерфейсе таких кнопок конечно нет).


  1. justvoice
    04.11.2021 01:22
    +5

    Спасибо, действительно ценные находки (в отличие от сотен статей вида «DevTools, издание 1024-е, скопипейсченное и отрерайченное» :))


  1. debagger
    04.11.2021 05:20
    +3

    Спасибо огромное, на редкость полезная и информативная статья!


  1. ua9msn
    04.11.2021 10:02
    +4

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


    1. faiwer Автор
      04.11.2021 11:18

      Но работает это только с отключенным sourceMap.

      Ах вот почему оно у меня не работало. Спасибо за наводку. Поиграю с этим.


  1. heavy
    04.11.2021 11:38
    +1

    Крутая статья!
    Помню как мучался с подменой урла в запросе, не додумался до такого решения.
    Спасибо!


  1. mrShadow
    04.11.2021 13:35
    +1

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

    Значение переменной, которая переименовалась в непойми что, иногда ещё можно выцепить из arguments. Я довольно часто этим пользуюсь


    1. faiwer Автор
      04.11.2021 13:56
      +1

      Неплохо. Правда не сработает для => функций.
      P.S. добавил в статью


  1. iFunk
    04.11.2021 15:37

    Кто то знает, как отключить новую фишку в стилях. Сейчас если оттуда скопировать стиль, то значения получаются с новой строки.

    К примеру:

    padding:

    5px 

    25px

    ;


    1. faiwer Автор
      04.11.2021 15:38

      Разве? Проверил у себя — и property-name и property-value на одной строке. Как вы копируете? Я просто выделил мышью и ctrl+C.


      padding: 5px 25px;


      1. NikolasPushkin
        04.11.2021 21:02

        Да, у меня тоже такая хрень с последним обновлением хрома пришла(( раньше проблема было только с transform: rotate, а теперь со всеми единицами измерения


    1. blizzardKv
      04.11.2021 22:25

      аналогично

      control c - control v ломает копипаст, вероятно связано с тем, что в последних версиях хрома добавили селект у величин padding/margin, чтобы вместо px подставить другую величину

      меня спасает райт-клик на копируемую величину и copy declaration https://puu.sh/In1Bl/9008744a7f.png


  1. 0Ld
    04.11.2021 21:48

    Спасибо автору, очень полезная статья. Утащил в сохранялки)


  1. ChernovKirilo
    05.11.2021 02:29
    +1

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

    Ну это уже слишком сложно, я в такой ситуации использовал print screen на widows, а потом сразу его отменял, это очень быстро, и не напрягает.


    1. faiwer Автор
      05.11.2021 02:31

      Если честно, то совсем непонятно, как print screen здесь может помочь. Я правда уже очень давно не пользовался Windows. Там при этом происходит что-то особенное? Появляется какое-нибудь окно, но при этом контрол не получает событие onBlur?


      1. ChernovKirilo
        05.11.2021 03:00
        +1

        На print scrteen появляется тёмный оверлей, и когда ты его закрываешь не создавая скриншот, браузер не воспринимает то что курсор ушёл.


        1. faiwer Автор
          05.11.2021 11:30

          Интересная штука, спасибо. 10 лет назад, кажется, overlay никаких не было и скриншот просто помещался в буфер обмена. Добавлю ваш рецепт в статью, спасибо.


          1. mayorovp
            05.11.2021 12:45
            +1

            Оно и сейчас так происходит если никаких программ не ставить.


    1. ChernovKirilo
      05.11.2021 03:10
      +1

      Так же хочу сказать что статья крутая, однозначно отправляю всем знакомым.

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

      Отличное решение, но вот снова таки - много писать, и не очень приятно. Мне кажется что в этом случае интуиция подсказала бы поставить console.log, и возможно это было бы лучше по удобству, иногда перезагрузить страницу удобнее и быстрее.


      1. faiwer Автор
        05.11.2021 11:28
        +1

        иногда перезагрузить страницу удобнее и быстрее.

        Тут целый ряд проблем:


        1. Вы можете ОЧЕНЬ СИЛЬНО не желать перезагружать страницу. Просто потому что смогли воспроизвести сложно-воспроизводимый баг. На это могут часы уйти. Состояние такой страницы на вес золота.
        2. На моём опыте очень часто страницы повисшие на стороне JS не перезагружаются. Они продолжают висеть и ни на что не реагировать. В тех случаях когда меня устраивает убиение страницы мне обычно приходится или гасить вкладку посредством Task Manager-а, или открывая новую и пытаясь убить старую крестиком (так часто срабатывает).

        Про console.log не понял. Как console.log поможет вам с зависшим JS?


    1. SniperDes
      06.11.2021 15:01
      +1

      Можно еще открыть DevTools в новой вкладке, открыть всплывашку, через Ctrl+Shift+C выбрать элемент всплывашки (в этот момент фокус перейдет на DevTools и Chrome не поймет, что фокус ушел и нужно закрыть всплывашку) и поставить break на удаление ноды. Тогда перед закрытием всплывашки будет вызван breakpoint и можно будет отлаживать всплывашку.


      1. faiwer Автор
        06.11.2021 15:03

        Вариант. Но, честно говоря, как-то сильно сложно, долго и муторно.


        1. JuSay
          07.11.2021 13:39
          +1

          Странный подход, но я им часто пользуюсь для ковыряние во всплывашках:

          открываем DevTools в том же окне. Делаем его по ширине таким, чтобы контекстное меню, вызванное правым кликом по элементу залазило на DevTools.

          Затем, кликаем по всплывашке и через правый клик, и через контекстное меню переводим мышь на DevTools.

          У меня работает с вероятностью 98%. Страница даже не понимает, что произошло :)


          1. faiwer Автор
            07.11.2021 13:53

            Интересный вариант. Решил опробовать на сложном примере. Не получается. <Select/>.<Dropdown/> закрывается. Кажется тут только остановка JS поможет. Но для более простых случаев должно сработать.


            1. JuSay
              07.11.2021 15:22
              +1

              Согласен, если закрывается от любого клика, то не спастисть.

              Если закрывается только от блюра, то срабатывает. Мне, видно, везло)


  1. mayorovp
    07.11.2021 15:10
    +1

    Ещё один способ избежать закрытия всплывающего окна при переключении в DevTools — открывать его не выходя из DevTools.


    1. faiwer Автор
      07.11.2021 15:16

      Через triggerEvent? Хорошая мысль. Я пока таким только в userscript-ах баловался. Но, пожалуй, вполне сгодится и для debug-а.


      1. mayorovp
        07.11.2021 15:37
        +1

        Да, например так. Ещё удобная фича есть что можно зафиксировать состояние :hover на любом элементе.


  1. faiwer Автор
    08.11.2021 12:01

    image


    Немного обновил статью. Оказалось, что в Chrome DevTools появились logPoints. Те же самые breakpoint-ы, но с единственной целью — логировать что-нибудь. Такой синтаксический сахар.


  1. kamre
    08.11.2021 16:36

    стоит убрать фокус из окна вкладки, как всплывашка тут же пропадает

    Разве Emulate Focused Page не для этого как раз предназначен? Чтобы без всех этих хитростей не терять фокус при работе с Dev Tools.


    1. faiwer Автор
      08.11.2021 16:39

      Вы про эту штуку?



      В некоторых случаях это поможет. Например если всплывашка работает на onMouseEnter/onMouseLeave. Или вы имеете ввиду что-то другое?


      1. kamre
        09.11.2021 06:30
        +2

        Нет, конечно. Эта штука про element, а есть глобальная настройка про page. Вызывается через Ctrl+Shift+P, пишется emulate и выбирается нужный пункт.

        Вот про это: https://tinytip.co/tips/devtools-focused-page/


        1. faiwer Автор
          14.11.2021 20:17

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