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 / Просмотреть код.

Основные панели:

Изменения, сделанные через DevTools, обычно существуют только в текущей вкладке. После перезагрузки страницы HTML и CSS снова загружаются с сервера.

Содержание


Панель Elements

Панель Elements показывает текущее DOM-дерево страницы и позволяет исследовать применённые CSS-стили.

DOM может отличаться от исходного HTML, потому что JavaScript способен добавлять, удалять и изменять элементы после загрузки документа.

Пример исходной разметки:

<article class="card">
  <h2 class="card__title">Товар</h2>
  <button class="button">Купить</button>
</article>

В панели Elements можно:

Выбор элемента

Нажмите кнопку выбора элемента в левом верхнем углу DevTools или используйте:

Ctrl + Shift + C

После этого наведите указатель на элемент страницы и щёлкните по нему.

Браузер покажет:

Временное изменение 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

Можно искать:

Примеры:

.card
button[type="submit"]
//button[contains(text(), "Сохранить")]

Панель Styles

Раздел Styles показывает CSS-правила, применяемые к выбранному элементу:

.button {
  padding: 12px 20px;
  color: white;
  background-color: #2563eb;
  border: 0;
}

В панели можно:

Переопределённое объявление отображается зачёркнутым:

color: red;

Оно может проигрывать из-за:

Панель 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:

Например, если класс неожиданно изменяется:

<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 используется для:

Простой вывод:

console.log("Приложение запущено");

Ошибка:

console.error("Не удалось загрузить данные");

Предупреждение:

console.warn("Используется устаревший метод");

Информационное сообщение:

console.info("Пользователь вошёл в систему");

Уровни сообщений

Консоль может показывать:

Фильтры помогают скрыть лишние записи и оставить только нужный уровень.

Выполнение JavaScript

В Console можно выполнять выражения в контексте открытой страницы:

document.title
window.location.href
document.querySelectorAll("button").length
localStorage.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 показывает запросы, которые страница отправляет по сети.

Чтобы увидеть запросы первоначальной загрузки:

  1. откройте Network;
  2. перезагрузите страницу;
  3. дождитесь завершения загрузки.

В таблице запросов обычно отображаются:

Поле Значение
Name Имя файла или адрес ресурса
Status Статус-код HTTP
Type Тип ресурса
Initiator Код, инициировавший запрос
Size Размер ответа
Time Общее время
Waterfall Этапы запроса на временной шкале

Типы запросов

Network позволяет фильтровать ресурсы:

Для анализа 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=true

Preview

Представляет ответ в удобном структурированном виде.

Например, JSON можно раскрывать как дерево:

{
  "id": 42,
  "name": "Анна",
  "roles": ["admin", "editor"]
}

Response

Показывает необработанное тело ответа:

{"id":42,"name":"Анна","roles":["admin","editor"]}

Initiator

Показывает, какой код или ресурс инициировал запрос.

Это помогает найти строку:

fetch("/api/users/42");

или зависимость, которая привела к загрузке ресурса.

Timing

Разделяет время запроса на этапы:

Большое время 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"

Перед публикацией такой команды необходимо удалить:

Блокировка запросов

Network request blocking позволяет временно блокировать адреса или шаблоны ресурсов.

Например:

*analytics*

или:

*.jpg

Это помогает проверить:

HAR-файл

Сетевой журнал можно экспортировать в формат HAR. Он содержит данные о запросах, ответах и времени выполнения.

HAR полезен для передачи информации об ошибке, но может содержать:

Перед передачей HAR-файл необходимо проверить и очистить от конфиденциальной информации.


Throttling и эмуляция сети

Throttling искусственно ограничивает скорость сети или процессора.

В Network можно выбрать профиль сети, например:

No throttling
Fast 4G
Slow 4G
Offline

Названия профилей зависят от версии браузера.

Эмуляция позволяет приблизительно проверить страницу при:

Проверка медленного соединения

Типичный сценарий:

  1. откройте Network;
  2. включите нужный сетевой профиль;
  3. при необходимости включите Disable cache;
  4. перезагрузите страницу;
  5. исследуйте Waterfall и поведение интерфейса.

Следует проверить:

Режим Offline

Режим Offline имитирует отсутствие сети.

Он помогает проверить:

Пример обработки ошибки:

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.

Это помогает обнаружить проблемы, незаметные на мощном компьютере:

Эмуляция не полностью заменяет проверку на реальном устройстве. Реальные устройства отличаются архитектурой процессора, объёмом памяти, браузером и поведением операционной системы.


Панель Sources

Панель Sources используется для просмотра загруженных файлов и пошаговой отладки JavaScript.

В ней можно:

Поиск файла

Быстро открыть файл:

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();

При достижении строки браузер остановит выполнение.

После остановки можно исследовать:

Управление выполнением

Основные команды отладчика:

Команда Назначение
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.name

Logpoint полезен, если нужно временно исследовать выполнение, не добавляя:

console.log(user);

в исходный файл.

Breakpoints на события

В разделе Event Listener Breakpoints можно остановить выполнение при возникновении события:

Например, если неизвестно, какой обработчик реагирует на кнопку:

  1. включите breakpoint для события click;
  2. нажмите кнопку;
  3. исследуйте остановившийся код и 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 позволяет постоянно вычислять выбранные выражения во время пошаговой отладки.

Примеры:

user
user.id
items.length
response.status
JSON.stringify(settings)

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

Watch удобно использовать для:

Следует избегать выражений с побочными эффектами:

items.pop()
counter++
saveUser()

Они могут изменить состояние программы во время отладки.


Scope

Раздел Scope показывает доступные области видимости.

Обычно отображаются:

Пример:

const applicationName = "Shop";

function createCounter() {
  let count = 0;

  return function increment() {
    count += 1;
    return count;
  };
}

Во время выполнения increment():

Значение переменной можно временно изменить через 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;
});

Но имя не появляется.

Последовательность проверки:

  1. Найдите кнопку в Elements.
  2. Убедитесь, что #load-user существует.
  3. Проверьте Event Listeners.
  4. Откройте Console и найдите ошибки.
  5. Откройте Network и включите фильтр Fetch/XHR.
  6. Нажмите кнопку.
  7. Проверьте, отправился ли запрос.
  8. Проверьте статус, тело ответа и заголовки.
  9. Установите breakpoint перед fetch().
  10. Выполните код по шагам.
  11. Проверьте response.ok.
  12. Исследуйте значение user.
  13. Убедитесь, что элемент #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 записывает работу страницы на временной шкале.

Она помогает находить:

Запись профиля

Типичный порядок:

  1. откройте Performance;
  2. включите нужное ограничение CPU;
  3. нажмите Record;
  4. выполните исследуемое действие;
  5. остановите запись;
  6. найдите длительные задачи;
  7. исследуйте Main thread и Call tree.

Следует записывать конкретный сценарий, например:

Слишком длинная запись содержит много шума и сложнее анализируется.

Главный поток

В основном потоке браузер выполняет:

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

Long Task

Задача продолжительностью более примерно 50ms считается долгой.

Во время такой задачи основной поток не может своевременно реагировать на события.

Пример блокирующего кода:

function calculate() {
  let result = 0;

  for (let index = 0; index < 100_000_000; index += 1) {
    result += Math.sqrt(index);
  }

  return result;
}

Возможные решения:

Flame chart

Flame chart показывает функции и длительность их выполнения.

Широкий блок означает, что операция заняла много времени.

Следует исследовать:

Различают:

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 объединяет отрисованные слои.

Частые или большие перерисовки могут быть вызваны:

Для анимации обычно предпочтительнее использовать:

transform
opacity

Вместо частого изменения:

top
left
width
height

Но создание чрезмерного количества отдельных слоёв также расходует память.

Rendering-инструменты

Дополнительная панель Rendering может предоставлять:

Подсветка Paint flashing помогает увидеть элементы, которые перерисовываются слишком часто.


Responsive Design Mode

Режим адаптивного дизайна эмулирует экран мобильного или планшетного устройства.

Включение:

Ctrl + Shift + M        Windows и Linux
Cmd + Shift + M         macOS

Команда работает, когда DevTools открыт.

В режиме эмуляции можно менять:

Произвольный размер

Можно указать собственные размеры:

375 × 812
768 × 1024
1440 × 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

Эмуляция сенсорного ввода помогает проверить:

Пример универсального события:

button.addEventListener("pointerdown", handlePointerDown);

Pointer Events могут работать с мышью, пером и сенсорным экраном.

Device Pixel Ratio

Высокий DPR означает, что одному CSS-пикселю соответствует несколько физических пикселей.

Это важно для:

Для адаптивных изображений можно использовать:

<img
  src="photo-800.jpg"
  srcset="
    photo-800.jpg 1x,
    photo-1600.jpg 2x
  "
  alt="Описание изображения"
>

Скриншоты

В режиме устройства можно создавать:

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

Ограничения эмуляции

Responsive Design Mode не воспроизводит полностью:

Финальную проверку критичных сценариев следует выполнять на реальных устройствах.


Работа с 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 можно удалить данные сайта:

Это полезно для проверки:

Очистка данных может привести к выходу из учётной записи и удалению несохранённых локальных данных.


Типичный сценарий анализа сетевой ошибки

Пусть интерфейс показывает сообщение:

Не удалось загрузить данные

Порядок диагностики:

  1. Откройте Console и проверьте ошибки JavaScript.
  2. Откройте Network.
  3. Включите Fetch/XHR.
  4. Повторите действие.
  5. Найдите запрос с красным статусом.
  6. Проверьте URL и HTTP-метод.
  7. Откройте Request Headers.
  8. Проверьте Content-Type и Authorization.
  9. Откройте Payload.
  10. Проверьте отправленные данные.
  11. Откройте Response.
  12. Прочитайте ответ сервера.
  13. Исследуйте Timing.
  14. Найдите Initiator.
  15. Перейдите в Sources и установите breakpoint.

Примеры причин:

400 — неверный формат запроса
401 — отсутствует или истёк токен
403 — недостаточно прав
404 — неправильный URL или ресурс отсутствует
415 — неверный Content-Type
422 — данные не прошли проверку
429 — превышен лимит запросов
500 — внутренняя ошибка сервера

Типичный сценарий поиска проблемы в CSS

Если элемент выглядит неправильно:

  1. Выберите его через инспектор.
  2. Проверьте Styles.
  3. Найдите зачёркнутые свойства.
  4. Проверьте специфичность селекторов.
  5. Откройте Computed.
  6. Найдите итоговое значение свойства.
  7. Проверьте бокс-модель.
  8. Включите :hover или :focus, если проблема связана с состоянием.
  9. Проверьте медиавыражения.
  10. Измените значение прямо в DevTools.
  11. Перенесите рабочее изменение в исходный CSS-файл.

Пример:

.card {
  width: 100%;
  max-width: 360px;
  padding: 20px;
}

Если карточка выходит за пределы контейнера, следует проверить:

box-sizing
width
min-width
padding
margin
overflow

Типичный сценарий поиска проблемы в JavaScript

Если действие не работает:

  1. Проверьте Console.
  2. Найдите первое исключение.
  3. Перейдите по ссылке на файл и строку.
  4. Установите breakpoint перед проблемным местом.
  5. Повторите действие.
  6. Проверьте Scope.
  7. Добавьте важные выражения в Watch.
  8. Исследуйте Call Stack.
  9. Выполните код через Step over.
  10. При необходимости зайдите внутрь функции через Step into.
  11. Проверьте запросы в Network.
  12. Исправьте причину в исходном коде.

Важно начинать с первой ошибки. Последующие ошибки могут быть только её следствием.


Полезные сочетания клавиш

Действие 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();

Основные правила: