Как создать скелетон экран на React + CSS
Одной из составляющих улучшения UX являются каркасные экраны. В этой статье мы рассмотрим скелетные загрузчики, их важность и способы их создания для вашего сайта с помощью React и vanilla CSS.
Чтобы забежать вперед:
- Что такое скелет CSS?
- Почему загрузчики скелетов важны?
- Когда использовать загрузчики скелетов
- Создание каркасного загрузчика с помощью React и CSSПредпосылки
- Настройка вашего приложения React
- HTML/CSS и React для сайта
- Скелетный загрузчик
- Анимация загрузчика скелетов
Что такое скелет CSS?
Давайте рассмотрим пример, а затем три сценария.
Практический пример: вы видите ссылку на эту статью и решаете нажать на нее. Для поступления данных требуется некоторое время.
Сценарий 1. Пока вы ждете, пока сайт отобразит статью, вы видите только пустой экран. Вы не знаете, как будет отображаться экран при загрузке, и вы не знаете, когда.
Сценарий 2. Когда данные загружаются, все, что вы видите, — это счетчик, указывающий, что данные находятся в пути. Вы знаете, что что-то придет после груза, но вы также не знаете, как это будет выглядеть и когда оно прибудет.
Сценарий 3. Когда страница загружается, вы замечаете эти серые фигуры, которые принимают структуру того, на что должна быть похожа страница. Пока вы ждете, вы уже знаете, как может выглядеть страница. Вы не знаете, когда придут данные, но ситуация заставляет вас ожидать, что они придут раньше, чем на самом деле.
Последний сценарий является частью так называемой «воспринимаемой производительности». Создается впечатление, что сайт загружается быстрее, чем есть на самом деле, потому что пользователь уже имеет представление о том, как он будет выглядеть.
Ниже приведен пример скелетного загрузчика для Facebook.

Как видите, серые фигуры показывают, как данные будут отображаться после загрузки. Когда данные наконец поступают с сервера, они заменяют серые фигуры на экране.
Скелетные загрузчики точно не увеличивают производительность или скорость загрузки. Они нужны только для того, чтобы дать пользователю что-то увидеть во время загрузки страницы и дать ему ощущение, что страница загружается быстрее.
Почему загрузчики скелетов важны?
Теперь давайте посмотрим, почему загрузчики скелетов важны и когда их следует использовать.
- Пользователи хотят быстрые и оптимизированные сайты. Загрузчики обеспечивают отличный пользовательский интерфейс, поскольку они сообщают пользователю, что данные получены и находятся в пути.
- Они разработаны таким образом, чтобы казалось, что данные загружаются быстрее, чем на самом деле.
Теперь вопрос в том, когда вы должны использовать скелетные загрузчики для своего сайта?
Когда использовать загрузчики скелетов
Вы можете использовать скелетные загрузчики, если:
- Ваш сайт имеет слишком большой трафик. Если слишком много пользователей постоянно используют ваш сайт, то в какой-то момент работа может показаться медленной. Загрузчики — отличный способ удержать пользователей на сайте
- Некоторые процессы требуют времени для завершения. Например, выборка данных из какой-либо базы данных или сервера
- Ваш сайт используется в удаленных местах с не очень хорошим подключением к Интернету. Некоторые люди утверждают, что загрузчики не должны использоваться, когда данные загружаются менее чем за 3 секунды.
Создание каркасного загрузчика с помощью React и CSS
Для руководства по этой статье мы создадим простую страницу, на которой будут отображаться статьи в виде карточек для конкретного блогера. Наш конечный результат будет выглядеть следующим образом.

Вид скелета:

Собственно сайт:

Предпосылки
Этот учебник очень удобен для начинающих, но есть некоторые детали, которые вам нужно проверить, чтобы вы могли свободно следовать им.
- Установите Node на свой компьютер. Вы можете проверить Node здесь , чтобы получить последнюю версию или загрузить ее, если у вас ее нет.
- Текстовый редактор. Я использую VS-код
- Базовые знания концепций React, таких как компоненты
- Компьютер с работающим браузером… а если нет, то что мы здесь делаем?
Настройка вашего приложения React
В этом уроке мы собираемся настроить наше приложение React совсем по-другому. Но сначала давайте красиво настроим наши каталоги.
- Запустите , а затем перейдите во вновь созданный каталог
mkdir cssLoader-Reactcd cssLoader-React - Вместо старого доброго , мы будем использовать инструмент под названием Vite , чтобы настроить это. Vite — это инструмент сборки, который помогает оптимизировать среду разработки. Он легкий и очень быстрый
npx create-react-app - Чтобы настроить Vite, введите в терминал. добавляет Vite в наш проект, а следующие за ним строки описывают название нашего проекта и устанавливают React для нас
npm create vite@latest vite-project -- --template reactnpm create vite@latest

Следующие инструкции просят нас запустить , чтобы перейти во вновь созданный каталог, добавить node_modules, чтобы установить наши зависимости, и запустить сервер.cd vite-projectnpm installnpm run dev
Если вы использовали Vite для настройки, то поймете, насколько удивительно быстро можно создать приложение React с нуля.
Откройте папку в любом текстовом редакторе по вашему выбору. Наше приложение настроено, и теперь пришло время добавить и удалить то, что нам не нужно, из нашего шаблона. Начнем с очистки всего от компонента App и файла index.css по умолчанию. Мы хотим создать две новые папки: папку с именем componentsвнутри папки и другую с именем в корневом каталоге. Наша новая структура проекта выглядит так:/srcserver

Причина, по которой у нас есть папка сервера, заключается в том, что мы собираемся создать структуру, подобную базе данных, где мы можем хранить данные о блогах. Не волнуйтесь, мы не будем заходить ни в какой бэкенд. Мы настроим поддельный REST API с JSON Server .
Итак , давайте создадим в папке файл с именем и заполним его некоторыми случайными данными, которые вы обычно видите в блоге, такими как аватар, имя автора, миниатюра, заголовок и описание. Обратите внимание, что папка создается в каталоге верхнего уровня или корневом каталоге./serverdb.json/server
В нашем файле содержимое должно выглядеть примерно так:db.json

Каждое свойство верхнего уровня считается ресурсом, и для каждого ресурса создаются конечные точки.
Используя пакет JSON Server, нам нужно наблюдать за этим файлом, чтобы мы могли создавать для него конечные точки и другие операции, такие как POST, GET, DELETE и т. д., которые мы можем захотеть выполнить (не рассматриваются в этой статье).
Еще больше замечательных статей от LogRocket:
- Не упустите момент с The Replay , кураторским информационным бюллетенем от LogRocket.
- Узнайте , как Galileo от LogRocket преодолевает шум, чтобы заблаговременно решать проблемы в вашем приложении
- Используйте useEffect React для оптимизации производительности вашего приложения.
- Переключение между несколькими версиями Node
- Узнайте, как анимировать ваше приложение React с помощью AnimXYZ
- Изучите Tauri , новый фреймворк для создания двоичных файлов
- Сравните NestJS и Express.js
Для этого откройте новый терминал и запустите . Если он работает успешно, то терминал должен запустить сервер на порту 5000 и начать следить за любыми изменениями.npx json-server -watch server/db.json -port 5000
Если вы скопируете и вставите http://localhost:5000 в браузере, вы увидите, что файл JSON был размещен на локальном хосте. Вы также должны увидеть http://localhost:5000/blogs в качестве ресурса.
После этого мы можем перейти к кодированию. Чтобы следить за кодом, вы можете просмотреть полные файлы и папки здесь .
HTML/CSS и React для сайта
Для начала давайте сначала получим данные с нашего локального сервера. После успешного извлечения и обработки любых ошибок выборки мы создадим шаблон для отображения данных.
Итак, в нашем :app.jsx
импортировать { useState , useEffect } из 'реагировать' ;
const App = () => { const [ блоги , setBlogs ] = useState ( null ); const [ ошибка , setError ] = useState ( false ); const [ загрузка , setLoading ] = useState ( true );
useEffect ( () => {
setTimeout ( () => {
fetch (
'http://localhost:5000/blogs' ) . then ( response => { if (! response . ok ){ throw Error ( «Извините, произошла ошибка при получении ваших блогов» ); } return response .json ( ); }) . затем ( данные => {
setBlogs ( данные );
setLoading ( false );
setError ( false ); })
.catch(err => {
console.log(err.message);
setError(true);
})
}, 4000)
})
return(
<div>
{blogs && <Blogs blogs = {blogs} /> }
{error && <div className='container'><span className='error'>Error connecting to the server. Connection failed.</span></div>}
</div>
)
}
экспортировать приложение по умолчанию ;
На нашей странице будет несколько элементов, которые будут меняться по мере того, как мы извлекаем и отображаем данные, поэтому нам нужен useStateхук для отслеживания этих элементов. Начнем с инициализации наших блогов нулем. Если данные не возвращаются, то нам нужно получить ошибку. Используя useStateснова, мы устанавливаем начальное состояние errorна false. Еще одна вещь, которую нам нужно инициализировать, — это loadingсостояние. Для этого было установлено значение true заранее, потому что данные должны быть загружены, прежде чем они вернутся к нам.
Теперь мы используем useEffectхук для получения наших данных после первого рендеринга страницы. Мы оборачиваем все в setTimeOut функцию, чтобы имитировать процесс выборки. В обычной ситуации вы бы так не поступили. Нам нужно это маленькое окно в четыре секунды, чтобы увидеть скелетный загрузчик.
Мы используем традиционную выборку для получения данных из нашего ресурса и устраняем ошибку, если код состояния отличается от 200. Если данные в хорошем состоянии, мы анализируем ответ в формате JSON. Поскольку это еще одно обещание, нам нужно разрешить его, используя другой метод. Здесь мы меняем состояние блогов с нуля на фактические данные, полученные с сервера. Поскольку данные получены и больше не «загружаются», мы меняем состояние с истинного на ложное. Если данные возвращаются, то ошибки нет, поэтому состояние тоже меняется..thenloading error
Внутри return() компонента мы имеем:
<div>
{блоги && < Блоги blogs = {блоги} /> }
{error && < div className = 'container' > < span className = 'error' > Ошибка подключения к серверу. Ошибка подключения. </ промежуток > </ дел > }
</ дел >
Первая строка просто означает, что, если blogsбыло возвращено и истинно, отобразить компонент с переданными ему возвращенными данными (в данном случае блогами). Логическое И (&&) гарантирует, что второй оператор будет выполнен только в том случае, если первый оператор верен. То же самое касается второй строки. Если состояние истинно, у нас есть div, который будет отображать сообщение об ошибке.<Blogs />error
Вскоре мы вернемся к аспекту loading.
Теперь мы создадим фактический Blogsкомпонент. Внутри папки создайте новый файл компонента с именем . /componentsblogs.jsx
const Blogs = ( { blogs } ) => { return ( < div className = "container" > { blogs .map ( blog => ( < div className = " blog" > <header> < div className = "avatar" > < img src ={ blog .avatar } alt = " черно-белая фотография улыбающегося мужчины перед ноутбуком" /> </ дел
>
< div className = "автор" > < p className = "имя" >{ blog . автор }</ p > </ div > </ header >
< main className = "image" > < img src = { blog . thumbnail } alt = "черный экран с кодом поверх него" /> </ main >
<нижний колонтитул> < p className = "title" >{ blog . title }</ p > < p className = "text" >{ blog . описание }</ p > </ footer > </ div > ))} </ div >
); }
экспортировать блоги по умолчанию ;
В этом компоненте мы переходим в blogsкачестве реквизита и идем дальше, чтобы создать шаблон для различных свойств нашего блога, таких как , и т. д.blog.titleblog.description
Мы избавились от стиля в нашем , поэтому мы можем заменить его на:index.css
@ URL -адрес импорта ( 'https://fonts.googleapis.com/css2?family=Poppins: wght@300 &display=swap' );
/* отображение блогов */ . контейнер { дисплей : сетка ; сетка - шаблон - столбцы : 6 fr 6 fr ; поле : 4em ; семейство шрифтов : ' Poppins ' , без засечек ; } . блог { нижняя граница : 2em ; _ } заголовок { дисплей : гибкий ; выровнять -
предметы : центр ; } . автор . имя { начертание шрифта : полужирный ; _ размер шрифта : крупный ; _ поле слева : 1rem ; _ } . заголовок { начертание шрифта : полужирный ; _ размер шрифта : 30px ; _ } . текст { вес шрифта : светлее ; _
font-size: large;
line-height:1em;
width: 25em;
}
footer{
line-height: 1rem;
}
.avatar img{
height: 80px;
width: 80px;
border-radius: 50%;
object-fit: cover;
}
. изображение изображение { высота : 70 % ; ширина : 70 % ; радиус границы : 8 пикселей ; } . ошибка { цвет : красный ; }
Это не что иное, как сетка и шрифт. Импортируйте Blogкомпонент внутри Appкомпонента, и теперь наш сайт блога должен выглядеть так:

Скелетный загрузчик
Для скелетного загрузчика нам нужно сначала создать скелетную структуру. Внутри папки давайте создадим новый компонент с именем ./componentsskeleton.jsx
const Skeleton = () => { return ( < div className = "skeleton-blog" > < header > < div className = "skeleton-avatar" > </ div >
< div classname = "skeleton-author" > </ div > </ header >
< main className = "skeleton-image" > </ main >
< cooler classname = "skeleton-footer" > < / cooler > </ div >
); };
экспортировать скелет по умолчанию ;
Это не что иное, как пустые элементы div, которые мы стилизуем так, чтобы они соответствовали форме нашего фактического сайта.
Еще в нашем : index.css
/* Скелетные стили*/ . скелет - блог { нижняя граница : 2em ; _ анимация : масштаб 4s бесконечный ; } . скелет - аватар { фон : #E5E4E2 ; высота : 60 пикселей ; ширина : 60 пикселей ; радиус границы : 50 % ; _ } . скелет - автор { фон :
#E5E4E2 ; высота : 30 пикселей ; ширина : 150 пикселей ; поле слева : 1rem ; _ радиус границы : 6px ; _ } . скелет- изображение { высота : 200 пикселей ; ширина : 320 пикселей ; радиус границы : 6px ; _ фон : #E5E4E2 ; поле - верх : 10px ; }
.skeleton-footer{
height: 30px;
width: 280px;
border-radius: 6px;
background: #E5E4E2;
margin-top: 10px;
}
Вернувшись в наш Appкомпонент, мы хотим сделать две вещи. Первым делом нужно импортировать компонент скелета вверху, а затем найти, где и как добавить скелеты.
<div className = "container" >
{загрузка && [ 1 , 2 , 3 , 4 , 5 , 6 , 7 ].map( ( n ) => <Скелетный ключ = {n} /> )}
< /div>
Точно так же, как мы реализовали ANDоператор раньше, мы сделаем то же самое и здесь. Помните, что loadingизначально было установлено значение true. Итак, пока loadingэто правда, мы хотим отображать скелеты, пока ждем поступления данных. Мы заключаем его в div с классом контейнера, чтобы он ссылался на наш index.css и придавал нашим скелетным картам такой же макет сетки..container
Вы заметите, что у нас происходит ситуация с массивом. Ваш массив может содержать любое количество элементов, которые вы хотите. В нашем случае мы хотим отображать семь карт скелета, пока наши данные загружаются. Обратите внимание, что скелетные загрузчики не предназначены для точной замены элементов на вашей странице — они просто временные заполнители. Если ваш сайт получает 1000 статей, отображение 1000 карточек кажется довольно амбициозным.
По сути, наши скелетные загрузчики должны выглядеть так во время загрузки страницы:

Через 4000 миллисекунд (4 секунды), как установлено нашей setTimeOutфункцией, фактические блоги должны быть загружены с сервера и отображены.
Анимация загрузчика скелетов
Некоторые загрузчики-скелеты имеют красивую мерцающую анимацию, которая быстро перемещается по странице. Давайте попробуем добавить простую анимацию масштабирования в наш загрузчик, чтобы придать ему хороший эффект увеличения и уменьшения масштаба.
Мы можем добиться этого, добавив одну строку CSS в наш класс, а затем добавив файл ..skeleton-blog@keyframe
. скелет - блог { анимация : масштаб 4s бесконечен ; }
Приведенное выше будет анимировать каждую карточку-скелет с некоторой анимацией, называемой scale, которую мы определили ниже.
/* Скелетная анимация*/ @keyframes scale { 0 % { transform : scale ( 0.9 )} 100 % { transform : scale ( 1 )} }
В сочетании они будут создавать анимацию масштабирования бесконечно (пока вы ждете данных) продолжительностью четыре секунды.
Все это в совокупности дает нам следующий результат:

Вывод
Я надеюсь, что эта статья сделала создание скелетного загрузчика проще и понятнее. Мы рассмотрели, что такое загрузчики, почему они важны, а также хороший и простой способ сделать свой собственный.
Вы можете проверить код для этого урока здесь . Не стесняйтесь вносить изменения, стилизовать его по своему усмотрению и вносить улучшения в код.
Ваш интерфейс загружает ЦП ваших пользователей?
Поскольку веб-интерфейсы становятся все более сложными, ресурсоемкие функции требуют от браузера все большего и большего. Если вы заинтересованы в мониторинге и отслеживании использования ЦП на стороне клиента, использования памяти и т. д. для всех ваших пользователей в рабочей среде, попробуйте LogRocket .
https://logrocket.com/signup/LogRocket похож на DVR для веб-приложений и мобильных приложений, записывая все, что происходит в вашем веб-приложении или на сайте. Вместо того, чтобы гадать, почему возникают проблемы, вы можете агрегировать и составлять отчеты по ключевым показателям производительности внешнего интерфейса, воспроизводить сеансы пользователей вместе с состоянием приложения, регистрировать сетевые запросы и автоматически отображать все ошибки.
Модернизируйте способы отладки веб-приложений и мобильных приложений — начните мониторинг бесплатно .
