DevTools и отладка
DevTools (Developer Tools) — встроенный в браузер набор инструментов для исследования веб-страниц, отладки JavaScript, анализа сетевых запросов, проверки адаптивной вёрстки и измерения производительности.
DevTools доступны в Chrome, Edge, Firefox и Safari. Названия панелей и отдельные функции могут отличаться, но основные возможности похожи.
Открыть DevTools можно несколькими способами:
F12
Ctrl + Shift + I Windows и Linux
Cmd + Option + I macOSОткрыть инспектор конкретного элемента:
Ctrl + Shift + C Windows и Linux
Cmd + Shift + C macOSОткрыть консоль:
Ctrl + Shift + J Windows и Linux
Cmd + Option + J macOSТакже можно нажать правой кнопкой мыши на элементе страницы и выбрать Inspect / Просмотреть код.
Основные панели:
- Elements — исследование и временное изменение HTML и CSS;
- Console — выполнение JavaScript и просмотр сообщений;
- Network — анализ сетевых запросов;
- Sources — просмотр исходников и отладка JavaScript;
- Performance — профилирование производительности;
- Application — просмотр хранилищ, cookie и других данных сайта.
Изменения, сделанные через DevTools, обычно существуют только в текущей вкладке. После перезагрузки страницы HTML и CSS снова загружаются с сервера.
Содержание
- Панель Elements
- Панель Console
- Расширенный Console API
- Панель Network
- Throttling и эмуляция сети
- Панель Sources
- Breakpoints
- Watch
- Scope
- Call Stack
- Практический пример отладки JavaScript
- Базовое профилирование производительности
- Responsive Design Mode
- Работа с localStorage
- Работа с sessionStorage
- Очистка данных сайта
- Типичный сценарий анализа сетевой ошибки
- Типичный сценарий поиска проблемы в CSS
- Типичный сценарий поиска проблемы в JavaScript
- Полезные сочетания клавиш
- Краткая памятка
Панель Elements
Панель Elements показывает текущее DOM-дерево страницы и позволяет исследовать применённые CSS-стили.
DOM может отличаться от исходного HTML, потому что JavaScript способен добавлять, удалять и изменять элементы после загрузки документа.
Пример исходной разметки:
<article class="card">
<h2 class="card__title">Товар</h2>
<button class="button">Купить</button>
</article>В панели Elements можно:
- искать элементы;
- изменять текст и атрибуты;
- добавлять и удалять HTML-узлы;
- редактировать CSS;
- проверять размеры и отступы;
- переключать состояния
:hover,:focusи другие; - отслеживать изменения DOM;
- просматривать обработчики событий.
Выбор элемента
Нажмите кнопку выбора элемента в левом верхнем углу DevTools или используйте:
Ctrl + Shift + CПосле этого наведите указатель на элемент страницы и щёлкните по нему.
Браузер покажет:
- границы элемента;
- ширину и высоту;
padding;border;margin;- соответствующий узел в DOM.
Временное изменение HTML
Текст узла можно изменить двойным щелчком:
<h2 class="card__title">Новое название</h2>Через контекстное меню доступна команда Edit as HTML, позволяющая изменить весь фрагмент:
<article class="card card--featured">
<h2 class="card__title">Новое название</h2>
<p class="card__description">Описание товара</p>
<button class="button">Добавить в корзину</button>
</article>Это удобно для быстрой проверки интерфейса, но изменение не сохраняется в исходном файле проекта.
Поиск в DOM
Откройте поиск:
Ctrl + F Windows и Linux
Cmd + F macOSМожно искать:
- обычный текст;
- CSS-селектор;
- XPath.
Примеры:
.cardbutton[type="submit"]//button[contains(text(), "Сохранить")]Панель Styles
Раздел Styles показывает CSS-правила, применяемые к выбранному элементу:
.button {
padding: 12px 20px;
color: white;
background-color: #2563eb;
border: 0;
}В панели можно:
- включать и отключать объявления;
- изменять значения;
- добавлять свойства;
- добавлять новые селекторы;
- видеть имя файла и номер строки;
- находить переопределённые свойства.
Переопределённое объявление отображается зачёркнутым:
color: red;Оно может проигрывать из-за:
- меньшей специфичности;
- более раннего расположения;
- правила с
!important; - неподходящего медиавыражения;
- наследования или другого источника значения.
Панель Computed
Раздел Computed показывает итоговые вычисленные значения CSS:
display: flex
font-size: 16px
line-height: 24px
width: 320pxЭто значения после применения:
- каскада;
- наследования;
- специфичности;
- медиавыражений;
- относительных единиц;
- стандартных стилей браузера.
Например, в исходном CSS может быть:
.title {
font-size: 2rem;
}Если корневой размер шрифта равен 16px, в Computed будет видно:
font-size: 32pxСтрелка рядом со свойством позволяет найти CSS-правило, из которого пришло значение.
Бокс-модель
В разделе Computed или Layout отображается бокс-модель элемента:
margin
border
padding
contentОна помогает находить причины:
- неожиданной ширины;
- лишних отступов;
- переполнения;
- смещения элементов;
- появления полосы прокрутки.
Например:
.card {
width: 300px;
padding: 20px;
border: 2px solid;
}При box-sizing: content-box итоговая ширина будет:
300px + 40px padding + 4px border = 344pxПри:
.card {
box-sizing: border-box;
}ширина вместе с padding и border останется равной 300px.
Принудительное состояние элемента
В панели Elements можно временно включить псевдоклассы:
:hover
:active
:focus
:focus-visible
:focus-within
:visitedДля этого выберите элемент и нажмите :hov в панели Styles.
Это удобно для отладки состояний:
.button:hover {
background-color: #1d4ed8;
}
.button:focus-visible {
outline: 3px solid #93c5fd;
}Некоторые состояния, особенно :visited, браузер ограничивает по соображениям конфиденциальности.
Просмотр обработчиков событий
Раздел Event Listeners показывает обработчики, связанные с выбранным элементом:
button.addEventListener("click", handleClick);Можно увидеть:
- тип события;
- файл и строку обработчика;
- элемент, на котором установлен обработчик;
- обработчики предков, участвующих во всплытии события.
Это полезно, если щелчок обрабатывается несколько раз или невозможно понять, какой код реагирует на событие.
DOM breakpoints
DevTools может приостановить JavaScript, когда выбранный DOM-узел изменяется.
В контекстном меню элемента выберите Break on:
- Subtree modifications — изменение потомков;
- Attribute modifications — изменение атрибутов;
- Node removal — удаление элемента.
Например, если класс неожиданно изменяется:
<div class="modal modal--hidden">установите breakpoint на изменение атрибутов. Выполнение остановится на строке JavaScript, которая меняет class.
Доступ к выбранному элементу
Выбранный в Elements элемент доступен в Console через переменную:
$0Ранее выбранные элементы:
$1
$2
$3
$4Примеры:
$0.textContent$0.getBoundingClientRect()$0.classList.add("active")getComputedStyle($0).displayПанель Console
Панель Console используется для:
- просмотра сообщений JavaScript;
- отображения ошибок и предупреждений;
- выполнения выражений;
- исследования переменных и объектов;
- взаимодействия с DOM;
- проверки небольших фрагментов кода.
Простой вывод:
console.log("Приложение запущено");Ошибка:
console.error("Не удалось загрузить данные");Предупреждение:
console.warn("Используется устаревший метод");Информационное сообщение:
console.info("Пользователь вошёл в систему");Уровни сообщений
Консоль может показывать:
- ошибки;
- предупреждения;
- информационные сообщения;
- отладочный вывод;
- сетевые сообщения.
Фильтры помогают скрыть лишние записи и оставить только нужный уровень.
Выполнение JavaScript
В Console можно выполнять выражения в контексте открытой страницы:
document.titlewindow.location.hrefdocument.querySelectorAll("button").lengthlocalStorage.getItem("theme")Многострочный код можно вводить с переносом строки:
Shift + EnterПовторно использовать результат последнего выражения можно через:
$_Пример:
document.querySelectorAll(".card")$_.lengthСокращения DevTools
В консоли Chromium-браузеров доступны вспомогательные команды.
Аналог document.querySelector():
$(".card")Аналог document.querySelectorAll() с массивоподобным результатом:
$$(".card")XPath-запрос:
$x("//button[contains(text(), 'Сохранить')]")Копирование значения в буфер обмена:
copy($0.outerHTML)Очистка консоли:
clear()Отображение объекта в виде раскрываемой структуры:
dir(document.body)Отслеживание событий:
monitorEvents($0, "click")Остановка отслеживания:
unmonitorEvents($0, "click")Эти команды предоставляются DevTools и обычно недоступны в обычном JavaScript-коде страницы.
Безопасность Console
Не следует вставлять в консоль код из неизвестного источника. Код выполняется с доступом к странице и может:
- читать данные интерфейса;
- выполнять действия от имени пользователя;
- обращаться к доступным хранилищам;
- отправлять сетевые запросы;
- изменять страницу.
Перед выполнением необходимо понимать назначение каждой инструкции.
Расширенный Console API
console.table()
Выводит массив или объект в виде таблицы:
const users = [
{
id: 1,
name: "Анна",
role: "admin"
},
{
id: 2,
name: "Иван",
role: "editor"
}
];
console.table(users);Можно выбрать отдельные столбцы:
console.table(users, ["name", "role"]);Объект также можно представить таблицей:
console.table({
frontend: {
status: "active",
developers: 4
},
backend: {
status: "active",
developers: 3
}
});console.group()
Объединяет сообщения в группу:
console.group("Загрузка пользователя");
console.log("Запрос отправлен");
console.log("Ответ получен");
console.log("Данные обработаны");
console.groupEnd();Свёрнутая по умолчанию группа:
console.groupCollapsed("Подробности запроса");
console.log({
method: "GET",
url: "/api/users/42"
});
console.groupEnd();Группы можно вкладывать:
console.group("Инициализация");
console.group("Настройки");
console.log("Тема загружена");
console.groupEnd();
console.group("Пользователь");
console.log("Профиль загружен");
console.groupEnd();
console.groupEnd();console.time()
Измеряет время между вызовами console.time() и console.timeEnd():
console.time("data-processing");
const result = items.map((item) => {
return item.value * 2;
});
console.timeEnd("data-processing");Промежуточное значение времени:
console.time("request");
await fetch("/api/users");
console.timeLog("request", "Ответ получен");
await loadAdditionalData();
console.timeEnd("request");Метки должны совпадать:
console.time("load");
console.timeEnd("load");Для серьёзного анализа производительности одной функции console.time() недостаточно. Она подходит для быстрых локальных измерений, а общий профиль страницы следует анализировать через Performance.
console.count()
Считает количество вызовов:
function handleClick() {
console.count("handleClick");
}Результат:
handleClick: 1
handleClick: 2
handleClick: 3Сброс счётчика:
console.countReset("handleClick");console.assert()
Выводит ошибку, если условие ложно:
console.assert(user.id > 0, "Некорректный ID пользователя");Можно передавать дополнительные данные:
console.assert(
response.ok,
"Запрос завершился с ошибкой",
response.status
);console.assert() не заменяет автоматические тесты и не останавливает выполнение программы.
console.trace()
Выводит стек вызовов:
function saveUser() {
console.trace("Вызвано сохранение пользователя");
}
function handleSubmit() {
saveUser();
}
handleSubmit();Команда помогает понять, откуда была вызвана функция.
console.dir()
Выводит объект как интерактивное дерево свойств:
console.dir(document.body);Это особенно удобно для DOM-элементов, которые console.log() может показывать как HTML.
console.clear()
Очищает видимую консоль:
console.clear();Записи также могут сохраняться, если включена опция Preserve log.
Панель Network
Панель Network показывает запросы, которые страница отправляет по сети.
Чтобы увидеть запросы первоначальной загрузки:
- откройте Network;
- перезагрузите страницу;
- дождитесь завершения загрузки.
В таблице запросов обычно отображаются:
| Поле | Значение |
|---|---|
| Name | Имя файла или адрес ресурса |
| Status | Статус-код HTTP |
| Type | Тип ресурса |
| Initiator | Код, инициировавший запрос |
| Size | Размер ответа |
| Time | Общее время |
| Waterfall | Этапы запроса на временной шкале |
Типы запросов
Network позволяет фильтровать ресурсы:
- Fetch/XHR — запросы JavaScript к API;
- Doc — HTML-документы;
- CSS — таблицы стилей;
- JS — JavaScript-файлы;
- Img — изображения;
- Font — шрифты;
- Media — аудио и видео;
- WS — WebSocket;
- Other — остальные запросы.
Для анализа REST API чаще всего используется фильтр Fetch/XHR.
Исследование запроса
После выбора запроса доступны вкладки.
Headers
Содержит общую информацию:
Request URL
Request Method
Status Code
Remote Address
Referrer PolicyЗдесь же отображаются заголовки запроса:
Accept: application/json
Authorization: Bearer access-token
Content-Type: application/json
Origin: https://app.example.comИ заголовки ответа:
Content-Type: application/json
Cache-Control: no-cache
ETag: "users-v3"Payload
Показывает отправленные данные.
JSON:
{
"name": "Анна",
"email": "anna@example.com"
}Параметры строки запроса:
page=2
limit=20
role=adminДанные формы:
username=anna
remember=truePreview
Представляет ответ в удобном структурированном виде.
Например, JSON можно раскрывать как дерево:
{
"id": 42,
"name": "Анна",
"roles": ["admin", "editor"]
}Response
Показывает необработанное тело ответа:
{"id":42,"name":"Анна","roles":["admin","editor"]}Initiator
Показывает, какой код или ресурс инициировал запрос.
Это помогает найти строку:
fetch("/api/users/42");или зависимость, которая привела к загрузке ресурса.
Timing
Разделяет время запроса на этапы:
- постановка в очередь;
- DNS lookup;
- установка TCP-соединения;
- TLS;
- отправка запроса;
- ожидание первого байта;
- загрузка ответа.
Большое время Waiting for server response, также называемое TTFB, может указывать на медленную серверную обработку, сетевую задержку или работу промежуточной инфраструктуры.
Большое время Content Download может быть связано с крупным ответом или медленным соединением.
Статус-коды
Успешные запросы:
200 OK
201 Created
204 No ContentПеренаправления и кэш:
301 Moved Permanently
304 Not ModifiedОшибки клиента:
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
422 Unprocessable Content
429 Too Many RequestsОшибки сервера:
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
504 Gateway TimeoutСтатус 304 Not Modified не является ошибкой. Он сообщает, что можно использовать кэшированную копию ресурса.
Поиск и фильтрация запросов
Фильтрация по части адреса:
usersФильтрация по методу:
method:POSTФильтрация по статусу:
status-code:404Фильтрация по домену:
domain:api.example.comПоддерживаемые фильтры зависят от браузера и его версии.
Preserve log
При переходе на другую страницу журнал Network обычно очищается.
Опция Preserve log сохраняет запросы между переходами и перезагрузками. Она полезна для анализа:
- перенаправлений;
- авторизации;
- перехода после отправки формы;
- запросов во время навигации;
- последовательности открытия нескольких страниц.
Disable cache
Опция Disable cache отключает HTTP-кэш, пока DevTools открыт.
Она полезна, если необходимо проверить загрузку всех ресурсов с сервера. Для исследования обычного пользовательского поведения её следует отключить, потому что пользователи обычно работают с включённым кэшем.
Повтор запроса
В контекстном меню запроса могут быть доступны команды:
Replay XHR
Copy as fetch
Copy as cURLПример скопированной команды:
curl \
"https://api.example.com/api/users/42" \
-H "Accept: application/json" \
-H "Authorization: Bearer access-token"Перед публикацией такой команды необходимо удалить:
- токены;
- cookie;
- персональные данные;
- внутренние адреса;
- секретные заголовки.
Блокировка запросов
Network request blocking позволяет временно блокировать адреса или шаблоны ресурсов.
Например:
*analytics*или:
*.jpgЭто помогает проверить:
- поведение без аналитического сервиса;
- резервный интерфейс при отсутствии изображения;
- зависимость страницы от стороннего скрипта;
- обработку ошибок загрузки.
HAR-файл
Сетевой журнал можно экспортировать в формат HAR. Он содержит данные о запросах, ответах и времени выполнения.
HAR полезен для передачи информации об ошибке, но может содержать:
- заголовки авторизации;
- cookie;
- параметры запросов;
- персональные данные;
- содержимое ответов.
Перед передачей HAR-файл необходимо проверить и очистить от конфиденциальной информации.
Throttling и эмуляция сети
Throttling искусственно ограничивает скорость сети или процессора.
В Network можно выбрать профиль сети, например:
No throttling
Fast 4G
Slow 4G
OfflineНазвания профилей зависят от версии браузера.
Эмуляция позволяет приблизительно проверить страницу при:
- низкой скорости загрузки;
- большой сетевой задержке;
- нестабильном соединении;
- полном отсутствии сети.
Проверка медленного соединения
Типичный сценарий:
- откройте Network;
- включите нужный сетевой профиль;
- при необходимости включите Disable cache;
- перезагрузите страницу;
- исследуйте Waterfall и поведение интерфейса.
Следует проверить:
- отображается ли индикатор загрузки;
- можно ли повторить неудачный запрос;
- не блокируется ли вся страница;
- загружается ли критический контент раньше второстепенного;
- не слишком ли велики изображения и JavaScript-файлы;
- корректно ли обрабатывается тайм-аут.
Режим Offline
Режим Offline имитирует отсутствие сети.
Он помогает проверить:
- сообщения об ошибке;
- повтор запроса;
- работу кэша;
- поведение Service Worker;
- офлайн-возможности приложения.
Пример обработки ошибки:
try {
const response = await fetch("/api/users");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const users = await response.json();
renderUsers(users);
} catch (error) {
showError("Не удалось загрузить пользователей");
}Ограничение CPU
В Performance и эмуляции устройств может быть доступно замедление CPU.
Это помогает обнаружить проблемы, незаметные на мощном компьютере:
- долгий запуск приложения;
- зависание интерфейса;
- тяжёлые обработчики событий;
- медленный рендеринг;
- слишком частые перерасчёты DOM.
Эмуляция не полностью заменяет проверку на реальном устройстве. Реальные устройства отличаются архитектурой процессора, объёмом памяти, браузером и поведением операционной системы.
Панель Sources
Панель Sources используется для просмотра загруженных файлов и пошаговой отладки JavaScript.
В ней можно:
- открывать JavaScript-файлы;
- ставить breakpoints;
- выполнять код по шагам;
- исследовать значения переменных;
- просматривать стек вызовов;
- останавливать выполнение на исключениях;
- искать код по всем исходникам.
Поиск файла
Быстро открыть файл:
Ctrl + P Windows и Linux
Cmd + P macOSПоиск текста по всем файлам:
Ctrl + Shift + F Windows и Linux
Cmd + Option + F macOSПереход к строке:
Ctrl + GМожно искать имя функции:
handleSubmitили часть URL:
/api/usersФорматирование минифицированного кода
Минифицированный код может выглядеть так:
function a(b){return b.map(c=>c*2)}Кнопка Pretty print, часто обозначаемая как {}, форматирует его:
function a(b) {
return b.map((c) => c * 2);
}Это облегчает чтение, но не восстанавливает исходные имена переменных.
Если проект публикует source maps, DevTools может показать исходные файлы TypeScript, JSX или неминифицированный JavaScript.
Breakpoints
Breakpoint — точка, в которой браузер приостанавливает выполнение JavaScript.
Пример кода:
async function loadUser(userId) {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
renderUser(user);
}Чтобы установить обычный breakpoint, щёлкните по номеру строки:
const user = await response.json();При достижении строки браузер остановит выполнение.
После остановки можно исследовать:
- локальные переменные;
- аргументы функции;
- глобальные значения;
- стек вызовов;
- текущий DOM;
- результаты выражений.
Управление выполнением
Основные команды отладчика:
| Команда | Назначение |
|---|---|
| Resume | Продолжить выполнение |
| Step over | Выполнить текущую строку, не заходя внутрь функции |
| Step into | Зайти внутрь вызываемой функции |
| Step out | Завершить текущую функцию и вернуться к вызывающему коду |
| Step | Выполнить следующий шаг |
| Deactivate breakpoints | Временно отключить breakpoints |
Часто используются сочетания:
F8 продолжить выполнение
F10 шаг с обходом функции
F11 шаг внутрь функции
Shift + F11 выйти из функцииСочетания могут отличаться в зависимости от браузера и операционной системы.
Breakpoint в коде
Инструкция debugger создаёт программную точку остановки:
function calculateTotal(items) {
debugger;
return items.reduce((sum, item) => {
return sum + item.price;
}, 0);
}Если DevTools открыт, выполнение остановится на строке:
debugger;Перед публикацией приложения такие инструкции обычно удаляют.
Условный breakpoint
Условный breakpoint срабатывает только при выполнении условия.
Например, цикл обрабатывает тысячу элементов:
for (const user of users) {
processUser(user);
}Можно остановиться только на пользователе с нужным ID:
user.id === 42Или только при некорректном значении:
user.email === undefinedУсловие не должно изменять состояние приложения. Нежелательно выполнять в нём функции с побочными эффектами.
Logpoint
Logpoint выводит сообщение в Console, не изменяя исходный код и обычно не останавливая программу.
Пример выражения:
"Обрабатывается пользователь:", user.id, user.nameLogpoint полезен, если нужно временно исследовать выполнение, не добавляя:
console.log(user);в исходный файл.
Breakpoints на события
В разделе Event Listener Breakpoints можно остановить выполнение при возникновении события:
click;submit;input;keydown;scroll;resize;load;- таймеры;
- события мыши;
- события клавиатуры.
Например, если неизвестно, какой обработчик реагирует на кнопку:
- включите breakpoint для события
click; - нажмите кнопку;
- исследуйте остановившийся код и Call Stack.
XHR/fetch breakpoints
Можно приостанавливать код при запросах, URL которых содержит определённую строку.
Например:
/api/usersОтладчик остановится, когда JavaScript попытается отправить подходящий XHR- или fetch-запрос.
Это помогает найти место, где создаётся неожиданный сетевой запрос.
Pause on exceptions
DevTools может останавливать выполнение при исключениях.
Режимы:
- остановка на необработанных исключениях;
- остановка на любых исключениях, включая обработанные.
Пример:
try {
JSON.parse("{ invalid json }");
} catch (error) {
console.error("Ошибка JSON", error);
}Если включена остановка на любых исключениях, отладчик остановится внутри JSON.parse(), даже несмотря на catch.
Watch
Раздел Watch позволяет постоянно вычислять выбранные выражения во время пошаговой отладки.
Примеры:
useruser.iditems.lengthresponse.statusJSON.stringify(settings)При переходе между строками значения обновляются.
Watch удобно использовать для:
- важных переменных;
- вложенных свойств;
- составных условий;
- длины коллекций;
- проверки состояния приложения.
Следует избегать выражений с побочными эффектами:
items.pop()counter++saveUser()Они могут изменить состояние программы во время отладки.
Scope
Раздел Scope показывает доступные области видимости.
Обычно отображаются:
- Local — локальные переменные текущей функции;
- Closure — значения из внешних функций;
- Script — переменные текущего скрипта;
- Global — глобальные значения.
Пример:
const applicationName = "Shop";
function createCounter() {
let count = 0;
return function increment() {
count += 1;
return count;
};
}Во время выполнения increment():
countбудет находиться вClosure;applicationNameможет быть доступно в глобальной или скриптовой области;- локальные значения функции будут показаны в
Local.
Значение переменной можно временно изменить через DevTools. Это полезно для проверки ветвей логики без перезапуска приложения.
Call Stack
Call Stack показывает цепочку функций, которая привела к текущей строке.
Пример:
function validateUser(user) {
if (!user.email) {
throw new Error("Email не указан");
}
}
function saveUser(user) {
validateUser(user);
}
function handleSubmit() {
saveUser({
name: "Анна"
});
}
handleSubmit();При остановке в validateUser() стек будет выглядеть примерно так:
validateUser
saveUser
handleSubmit
anonymousВерхний элемент — текущая функция. Ниже находятся вызывающие функции.
Щелчок по записи стека позволяет:
- перейти к месту вызова;
- просмотреть локальные переменные того уровня;
- понять путь выполнения программы.
Асинхронный стек
При использовании промисов и async/await отладчик может показывать асинхронную цепочку:
async function loadProfile() {
const response = await fetch("/api/profile");
return response.json();
}
async function initializeApplication() {
const profile = await loadProfile();
renderProfile(profile);
}Асинхронный стек помогает связать продолжение после await с кодом, запустившим операцию.
Практический пример отладки JavaScript
Пусть кнопка должна загружать пользователя:
const button = document.querySelector("#load-user");
button.addEventListener("click", async () => {
const response = await fetch("/api/users/42");
const user = await response.json();
document.querySelector("#user-name").textContent = user.name;
});Но имя не появляется.
Последовательность проверки:
- Найдите кнопку в Elements.
- Убедитесь, что
#load-userсуществует. - Проверьте Event Listeners.
- Откройте Console и найдите ошибки.
- Откройте Network и включите фильтр Fetch/XHR.
- Нажмите кнопку.
- Проверьте, отправился ли запрос.
- Проверьте статус, тело ответа и заголовки.
- Установите breakpoint перед
fetch(). - Выполните код по шагам.
- Проверьте
response.ok. - Исследуйте значение
user. - Убедитесь, что элемент
#user-nameнайден.
Более безопасный вариант кода:
const button = document.querySelector("#load-user");
const userName = document.querySelector("#user-name");
button.addEventListener("click", async () => {
try {
const response = await fetch("/api/users/42");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const user = await response.json();
if (!userName) {
throw new Error("Элемент #user-name не найден");
}
userName.textContent = user.name;
} catch (error) {
console.error("Не удалось загрузить пользователя", error);
}
});Базовое профилирование производительности
Панель Performance записывает работу страницы на временной шкале.
Она помогает находить:
- долгий JavaScript;
- блокировку главного потока;
- частые перерасчёты стилей;
- дорогую компоновку;
- медленный рендеринг;
- лишние перерисовки;
- задержки реакции на действия пользователя;
- проблемы анимации.
Запись профиля
Типичный порядок:
- откройте Performance;
- включите нужное ограничение CPU;
- нажмите Record;
- выполните исследуемое действие;
- остановите запись;
- найдите длительные задачи;
- исследуйте Main thread и Call tree.
Следует записывать конкретный сценарий, например:
- открытие модального окна;
- ввод текста в поиск;
- прокрутку списка;
- переключение вкладки;
- отправку формы.
Слишком длинная запись содержит много шума и сложнее анализируется.
Главный поток
В основном потоке браузер выполняет:
- JavaScript;
- вычисление стилей;
- Layout;
- Paint;
- часть работы рендеринга.
Если главный поток долго занят, браузер не может быстро обработать пользовательский ввод.
Long Task
Задача продолжительностью более примерно 50ms считается долгой.
Во время такой задачи основной поток не может своевременно реагировать на события.
Пример блокирующего кода:
function calculate() {
let result = 0;
for (let index = 0; index < 100_000_000; index += 1) {
result += Math.sqrt(index);
}
return result;
}Возможные решения:
- уменьшить объём вычислений;
- разделить работу на части;
- кэшировать результаты;
- перенести тяжёлые вычисления в Web Worker;
- избегать лишней обработки;
- выполнять работу только при необходимости.
Flame chart
Flame chart показывает функции и длительность их выполнения.
Широкий блок означает, что операция заняла много времени.
Следует исследовать:
- самые широкие функции;
- повторяющиеся вызовы;
- глубокие цепочки;
- обработчики событий;
- частые операции с DOM.
Различают:
- Self time — время внутри самой функции;
- Total time — время функции вместе с вызванными функциями.
Recalculate Style
Браузер пересчитывает стили после изменений DOM или CSS.
Большое число перерасчётов может возникнуть при частом переключении классов:
for (const element of elements) {
element.classList.add("active");
}Обычно лучше группировать изменения и избегать чередования чтения и записи геометрии.
Layout
Layout определяет размеры и положение элементов.
Неэффективный пример:
for (const element of elements) {
const width = element.offsetWidth;
element.style.width = `${width + 10}px`;
}Здесь чтение размеров и изменение стилей чередуются. Это может вызывать повторные синхронные вычисления Layout.
Лучше сначала собрать размеры, а затем применить изменения:
const widths = elements.map((element) => {
return element.offsetWidth;
});
elements.forEach((element, index) => {
element.style.width = `${widths[index] + 10}px`;
});Paint и Composite
Paint создаёт визуальное представление элементов.
Composite объединяет отрисованные слои.
Частые или большие перерисовки могут быть вызваны:
- изменением больших фоновых областей;
- сложными тенями;
- фильтрами;
- анимацией геометрических свойств;
- частыми изменениями DOM.
Для анимации обычно предпочтительнее использовать:
transform
opacityВместо частого изменения:
top
left
width
heightНо создание чрезмерного количества отдельных слоёв также расходует память.
Rendering-инструменты
Дополнительная панель Rendering может предоставлять:
- подсветку перерисовываемых областей;
- отображение границ слоёв;
- FPS meter;
- эмуляцию
prefers-reduced-motion; - эмуляцию цветовых особенностей;
- эмуляцию печатных стилей.
Подсветка Paint flashing помогает увидеть элементы, которые перерисовываются слишком часто.
Responsive Design Mode
Режим адаптивного дизайна эмулирует экран мобильного или планшетного устройства.
Включение:
Ctrl + Shift + M Windows и Linux
Cmd + Shift + M macOSКоманда работает, когда DevTools открыт.
В режиме эмуляции можно менять:
- ширину и высоту экрана;
- масштаб;
- ориентацию;
- Device Pixel Ratio;
- сенсорный ввод;
- User-Agent;
- тип устройства;
- сетевые ограничения.
Произвольный размер
Можно указать собственные размеры:
375 × 812768 × 10241440 × 900Полезно проверять не только стандартные устройства, но и промежуточные ширины. Интерфейс должен оставаться корректным между breakpoint-значениями.
Breakpoints в адаптивной вёрстке
Пример медиавыражения:
.layout {
display: grid;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.layout {
grid-template-columns: 240px 1fr;
}
}Проверять следует ширины:
767px
768px
769pxЭто помогает обнаружить ошибку непосредственно на границе медиавыражения.
Ориентация
Эмулятор позволяет переключать:
portrait
landscapeИнтерфейс необходимо проверять в обеих ориентациях, особенно если используются:
100vh
position: fixedВысота области просмотра на реальном мобильном устройстве может изменяться из-за адресной строки и элементов браузера.
Для современных интерфейсов могут использоваться динамические единицы:
.page {
min-height: 100dvh;
}Touch simulation
Эмуляция сенсорного ввода помогает проверить:
- размеры интерактивных элементов;
- отсутствие зависимости только от
:hover; - жесты;
- обработчики touch- и pointer-событий;
- прокрутку.
Пример универсального события:
button.addEventListener("pointerdown", handlePointerDown);Pointer Events могут работать с мышью, пером и сенсорным экраном.
Device Pixel Ratio
Высокий DPR означает, что одному CSS-пикселю соответствует несколько физических пикселей.
Это важно для:
- растровых изображений;
- canvas;
- скриншотов;
- тонких линий;
- проверки чёткости графики.
Для адаптивных изображений можно использовать:
<img
src="photo-800.jpg"
srcset="
photo-800.jpg 1x,
photo-1600.jpg 2x
"
alt="Описание изображения"
>Скриншоты
В режиме устройства можно создавать:
- снимок видимой области;
- снимок выбранного размера;
- полноразмерный снимок страницы.
Скриншоты удобны для проверки макета, но не заменяют визуальное тестирование на реальном устройстве.
Ограничения эмуляции
Responsive Design Mode не воспроизводит полностью:
- производительность реального телефона;
- объём памяти;
- особенности мобильной операционной системы;
- физическую клавиатуру;
- поведение экранного ввода;
- все особенности Safari и других браузеров;
- реальную нестабильность мобильной сети.
Финальную проверку критичных сценариев следует выполнять на реальных устройствах.
Работа с localStorage
localStorage — хранилище пар «ключ — значение», связанное с origin сайта.
Оно сохраняется после перезагрузки страницы и обычно остаётся после закрытия браузера.
Открыть данные можно через:
Application → Storage → Local StorageВыберите нужный origin, например:
https://app.example.comВ таблице будут показаны ключи и значения.
Пример:
| Key | Value |
|---|---|
theme |
dark |
language |
ru |
userSettings |
{"compact":true} |
В DevTools можно:
- добавлять записи;
- редактировать ключи и значения;
- удалять отдельные записи;
- очищать всё хранилище;
- искать значения.
Работа через Console
Записать значение:
localStorage.setItem("theme", "dark");Получить значение:
localStorage.getItem("theme");Удалить значение:
localStorage.removeItem("theme");Очистить хранилище текущего origin:
localStorage.clear();Получить количество записей:
localStorage.length;Получить ключ по индексу:
localStorage.key(0);Хранение объектов
localStorage хранит только строки.
Неправильно:
localStorage.setItem("settings", {
compact: true
});Будет сохранена строка:
[object Object]Правильная запись:
const settings = {
compact: true,
language: "ru"
};
localStorage.setItem(
"settings",
JSON.stringify(settings)
);Чтение:
const rawSettings = localStorage.getItem("settings");
const settings = rawSettings
? JSON.parse(rawSettings)
: null;Безопасная обработка повреждённого JSON:
function readSettings() {
const rawSettings = localStorage.getItem("settings");
if (!rawSettings) {
return null;
}
try {
return JSON.parse(rawSettings);
} catch (error) {
console.error(
"Не удалось прочитать настройки",
error
);
return null;
}
}Событие storage
При изменении localStorage в одной вкладке другие вкладки того же origin могут получить событие:
window.addEventListener("storage", (event) => {
console.log("Ключ:", event.key);
console.log("Старое значение:", event.oldValue);
console.log("Новое значение:", event.newValue);
});В той же вкладке, которая выполнила изменение, событие обычно не возникает.
Работа с sessionStorage
sessionStorage также хранит строковые пары «ключ — значение», но связано с конкретной вкладкой и сеансом страницы.
Открыть его можно через:
Application → Storage → Session StorageЗаписать значение:
sessionStorage.setItem("currentStep", "2");Прочитать значение:
sessionStorage.getItem("currentStep");Удалить значение:
sessionStorage.removeItem("currentStep");Очистить:
sessionStorage.clear();Различие localStorage и sessionStorage
| Характеристика | localStorage | sessionStorage |
|---|---|---|
| Формат | Строковые ключи и значения | Строковые ключи и значения |
| Сохраняется после перезагрузки | Да | Да |
| Сохраняется после закрытия вкладки | Обычно да | Нет |
| Разделяется между вкладками одного origin | Обычно да | Обычно нет |
| Доступно из JavaScript | Да | Да |
Поведение при копировании или восстановлении вкладок может зависеть от браузера.
Ограничения безопасности
localStorage и sessionStorage доступны JavaScript-коду страницы. При XSS-уязвимости злоумышленник может прочитать находящиеся там данные.
Не рекомендуется хранить в Web Storage:
- пароли;
- долгоживущие токены;
- банковские данные;
- другие чувствительные секреты.
Хранилище также ограничено по объёму, а его синхронный API может блокировать основной поток при работе с большими данными.
Для значительного объёма структурированных данных обычно лучше подходит IndexedDB.
Очистка данных сайта
В панели Application можно удалить данные сайта:
- localStorage;
- sessionStorage;
- IndexedDB;
- Cache Storage;
- cookie;
- Service Worker;
- другие данные origin.
Это полезно для проверки:
- первого запуска;
- поведения после выхода из аккаунта;
- миграции настроек;
- работы без кэша;
- повреждённых пользовательских данных.
Очистка данных может привести к выходу из учётной записи и удалению несохранённых локальных данных.
Типичный сценарий анализа сетевой ошибки
Пусть интерфейс показывает сообщение:
Не удалось загрузить данныеПорядок диагностики:
- Откройте Console и проверьте ошибки JavaScript.
- Откройте Network.
- Включите Fetch/XHR.
- Повторите действие.
- Найдите запрос с красным статусом.
- Проверьте URL и HTTP-метод.
- Откройте Request Headers.
- Проверьте
Content-TypeиAuthorization. - Откройте Payload.
- Проверьте отправленные данные.
- Откройте Response.
- Прочитайте ответ сервера.
- Исследуйте Timing.
- Найдите Initiator.
- Перейдите в Sources и установите breakpoint.
Примеры причин:
400 — неверный формат запроса
401 — отсутствует или истёк токен
403 — недостаточно прав
404 — неправильный URL или ресурс отсутствует
415 — неверный Content-Type
422 — данные не прошли проверку
429 — превышен лимит запросов
500 — внутренняя ошибка сервераТипичный сценарий поиска проблемы в CSS
Если элемент выглядит неправильно:
- Выберите его через инспектор.
- Проверьте Styles.
- Найдите зачёркнутые свойства.
- Проверьте специфичность селекторов.
- Откройте Computed.
- Найдите итоговое значение свойства.
- Проверьте бокс-модель.
- Включите
:hoverили:focus, если проблема связана с состоянием. - Проверьте медиавыражения.
- Измените значение прямо в DevTools.
- Перенесите рабочее изменение в исходный CSS-файл.
Пример:
.card {
width: 100%;
max-width: 360px;
padding: 20px;
}Если карточка выходит за пределы контейнера, следует проверить:
box-sizing
width
min-width
padding
margin
overflowТипичный сценарий поиска проблемы в JavaScript
Если действие не работает:
- Проверьте Console.
- Найдите первое исключение.
- Перейдите по ссылке на файл и строку.
- Установите breakpoint перед проблемным местом.
- Повторите действие.
- Проверьте Scope.
- Добавьте важные выражения в Watch.
- Исследуйте Call Stack.
- Выполните код через Step over.
- При необходимости зайдите внутрь функции через Step into.
- Проверьте запросы в Network.
- Исправьте причину в исходном коде.
Важно начинать с первой ошибки. Последующие ошибки могут быть только её следствием.
Полезные сочетания клавиш
| Действие | Windows и Linux | macOS |
|---|---|---|
| Открыть DevTools | F12 или Ctrl + Shift + I |
Cmd + Option + I |
| Выбрать элемент | Ctrl + Shift + C |
Cmd + Shift + C |
| Открыть Console | Ctrl + Shift + J |
Cmd + Option + J |
| Режим устройства | Ctrl + Shift + M |
Cmd + Shift + M |
| Найти в текущей панели | Ctrl + F |
Cmd + F |
| Найти по исходникам | Ctrl + Shift + F |
Cmd + Option + F |
| Быстро открыть файл | Ctrl + P |
Cmd + P |
| Продолжить выполнение | F8 |
F8 |
| Step over | F10 |
F10 |
| Step into | F11 |
F11 |
| Step out | Shift + F11 |
Shift + F11 |
Некоторые клавиши могут перехватываться операционной системой или настройками ноутбука.
Краткая памятка
Назначение основных панелей:
Elements — HTML, DOM, CSS, размеры и события
Console — сообщения, ошибки и выполнение JavaScript
Network — запросы, ответы, заголовки и время загрузки
Sources — исходники, breakpoints и пошаговая отладка
Performance — JavaScript, рендеринг и нагрузка на главный поток
Application — localStorage, sessionStorage, cookie и кэшиОсновные инструменты отладки JavaScript:
Breakpoint — остановка на строке
Conditional breakpoint — остановка по условию
Logpoint — вывод без изменения исходного кода
Watch — наблюдение за выражениями
Scope — переменные текущих областей видимости
Call Stack — цепочка вызовов
Pause on exceptions — остановка на исключенияхПолезные команды Console:
console.table(data);
console.group("Группа");
console.groupEnd();
console.time("operation");
console.timeEnd("operation");
console.count("handler");
console.assert(value !== null, "Значение отсутствует");
console.trace("Стек вызовов");Работа с хранилищами:
localStorage.setItem("key", "value");
localStorage.getItem("key");
localStorage.removeItem("key");
localStorage.clear();
sessionStorage.setItem("key", "value");
sessionStorage.getItem("key");
sessionStorage.removeItem("key");
sessionStorage.clear();Основные правила:
- начинайте диагностику с первой ошибки в Console;
- проверяйте реальные запросы в Network;
- используйте breakpoints вместо большого количества
console.log; - не выполняйте в Watch выражения с побочными эффектами;
- проверяйте интерфейс на промежуточных размерах экрана;
- тестируйте медленную сеть, Offline и замедленный CPU;
- сравнивайте поведение с кэшем и без него;
- не публикуйте HAR, cURL и скриншоты с токенами или персональными данными;
- не вставляйте в Console неизвестный код;
- не храните чувствительные секреты в localStorage и sessionStorage;
- переносите найденные через DevTools исправления в исходные файлы проекта;
- подтверждайте критичные результаты эмуляции на реальных устройствах.