Практика: fetch и построение DOM

Основы фронтенд разработки

Практика: fetch и построение DOM

Продолжительность: 25 мин

В уроке про fetch и async/await мы разобрали, как отправить запрос и получить данные. Теперь закроем практическую сторону: загрузим реальные данные с сервера и построим из них DOM-элементы прямо на странице.

Задача

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

GET https://advanced-frontend.ru/api/frontend-basics/attractions

Ответ — массив объектов, каждый из которых описывает одно место:

[ { "id": 1, "name": "Эрмитаж", "description": "Один из крупнейших и старейших художественных музеев мира, расположенный в Зимнем дворце на Дворцовой площади.", "category": "museum", "rating": 4.9, "image": "https://advanced-frontend.ru/api/frontend-basics/attractions/hermitage.jpg" }, ... ]
json

Наша задача — загрузить эти данные и отрисовать карточки на странице.

Структура HTML

Добавьте контейнер, в который будем вставлять карточки:

<section class="attractions-section"> <div class="container"> <h2>Достопримечательности</h2> <div class="attractions-grid" id="attractionsGrid"> <!-- карточки появятся здесь --> </div> </div> </section>
html

Состояния загрузки

Хороший интерфейс всегда показывает пользователю, что происходит. При работе с сетевыми запросами есть три состояния:

  • Загружается — данные ещё в пути, показываем индикатор
  • Успех — данные пришли, рисуем карточки
  • Ошибка — что-то пошло не так, сообщаем об этом

Это стандартный паттерн, который вы будете встречать в любом проекте.

Создаём карточку из объекта

Напишем функцию, которая принимает один объект достопримечательности и возвращает готовый DOM-элемент:

function createAttractionCard(attraction) { const card = document.createElement('article'); card.classList.add('attraction-card'); card.innerHTML = ` <div class="attraction-card__image"> <img src="${attraction.image}" alt="${attraction.name}" loading="lazy"> <span class="attraction-card__category">${attraction.category}</span> </div> <div class="attraction-card__body"> <h3 class="attraction-card__title">${attraction.name}</h3> <p class="attraction-card__description">${attraction.description}</p> <div class="attraction-card__rating"> ${attraction.rating} </div> </div> `; return card; }
js

Обратите внимание: innerHTML здесь безопасен, потому что данные приходят с нашего собственного сервера — мы контролируем их содержимое. Если бы данные вводил пользователь, нужно было бы использовать textContent для каждого поля.

Загружаем и отрисовываем

const grid = document.querySelector('#attractionsGrid'); async function loadAttractions() { // Показываем состояние загрузки grid.innerHTML = '<p class="loading-text">Загружаем достопримечательности...</p>'; try { const response = await fetch('https://advanced-frontend.ru/api/frontend-basics/attractions'); if (!response.ok) { throw new Error(`Ошибка сервера: ${response.status}`); } const attractions = await response.json(); // Очищаем контейнер grid.innerHTML = ''; if (attractions.length === 0) { grid.innerHTML = '<p>Достопримечательности не найдены.</p>'; return; } // Строим фрагмент — вставляем всё за один раз const fragment = document.createDocumentFragment(); attractions.forEach((attraction) => { fragment.append(createAttractionCard(attraction)); }); grid.append(fragment); } catch (error) { console.error(error); grid.innerHTML = ` <div class="error-state"> <p>Не удалось загрузить данные. Попробуйте обновить страницу.</p> <button onclick="loadAttractions()">Повторить</button> </div> `; } } loadAttractions();
js

Почему DocumentFragment?

Вместо того чтобы добавлять карточки по одной через append в цикле, мы сначала складываем их в DocumentFragment — это легковесный контейнер, который не привязан к DOM. Когда все карточки готовы, добавляем весь фрагмент за один вызов append. Результат тот же, но браузер перерисовывает страницу один раз вместо N раз — это важно при большом количестве элементов.

Кэшируем результат в localStorage

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

const CACHE_KEY = 'attractions-cache'; const CACHE_TTL = 10 * 60 * 1000; // 10 минут в миллисекундах async function loadAttractions() { // Проверяем кэш const cached = localStorage.getItem(CACHE_KEY); if (cached) { const { data, timestamp } = JSON.parse(cached); if (Date.now() - timestamp < CACHE_TTL) { renderAttractions(data); return; } } grid.innerHTML = '<p class="loading-text">Загружаем достопримечательности...</p>'; try { const response = await fetch('https://advanced-frontend.ru/api/frontend-basics/attractions'); if (!response.ok) throw new Error(`Ошибка: ${response.status}`); const attractions = await response.json(); // Сохраняем в кэш localStorage.setItem(CACHE_KEY, JSON.stringify({ data: attractions, timestamp: Date.now(), })); renderAttractions(attractions); } catch (error) { console.error(error); grid.innerHTML = '<p class="error-state">Не удалось загрузить данные.</p>'; } } function renderAttractions(attractions) { grid.innerHTML = ''; const fragment = document.createDocumentFragment(); attractions.forEach((a) => fragment.append(createAttractionCard(a))); grid.append(fragment); }
js

TTL (time to live) — время жизни кэша. По истечении 10 минут следующий запрос снова пойдёт на сервер и обновит данные.

Итого

Загрузка данных с сервера и построение DOM из них — один из самых частых паттернов в frontend-разработке. Схема всегда одинаковая:

  1. Показать состояние загрузки
  2. Сделать fetch
  3. Проверить response.ok
  4. Построить DOM-элементы из данных
  5. Обработать ошибку в catch

Всё остальное — детали конкретной задачи. В следующем модуле применим этот паттерн в полноценном проекте.

Это платный урок

Купите полный доступ к курсу чтобы просматривать данный контент

Основы фронтенд разработки

Полный курс по основам веб-разработки с нуля: HTML, CSS и JavaScript. Вы сверстаете адаптивный лендинг, освоите анимации и работу с DOM, а в финале соберёте самостоятельный проект — интерактивный путеводитель по Санкт-Петербургу.

4990 Скидка 20%
3990

Безопасные платежи обрабатываются сервисом Юкасса

Комментарии

Войдите, чтобы оставить комментарий