Использование техники content placeholder

Сегодня я хочу рассказать о довольно интересном подходе, который называется content placeholder. Порой возникает ситуация, когда нужно подгрузить какие-нибудь данные на страницу. В этом период времени пользователю необходимо ждать какое-то время. Но мы можем уменьшить это ощущение ожидания. Как? Предоставляя пользователю некое представление о том, как будет выглядеть страница, когда контент подгрузится.

В конце у нас получится что-то типо такого:

Этот способ намного лучше обычного спиннера, потому что он показывает сразу, как будет располагаться контент. Это так же отлично подходит для offline first приложений. И еще один аргумент за использование этого подхода, его сейчас используют все: ютуб, инстаграм, фейсбук, медиум, вайбер, можно перечислять и перечислять.

Типичная реализация на одном из веб-приложений, в котором я принимал участие:

Или можете перейти в этом блоге на главную, а потом на какой-нибудь еще не загруженный пост, и увидите тот же самый эффект.

Перейдем сразу к реализации: реализации на react и typescript. Начнем с компонента Rect (Rectangle, прямоугольник), который будет просто рисовать прямоугольник, все просто.

Библиотека b_ используется для формирования классов, согласно методологии BEM.

import React, { StatelessComponent } from 'react';
import b from 'b_';
import './rect.css';

const class_ = b.with('rect');

interface RectProps {
    type?: string;
    width?: number | string;
    height?: number | string;
}

const Rect: StatelessComponent<RectProps> = (props: RectProps) => {
    const { width, height, type = 'default' } = props;

    return (
        <div style={ { width, height } } className={class_({ type })} />
    );
};

export default Rect;

Обычный функциональный компонент, который принимает ширину, высоту и тип блока.

Перейдем к css. Добавам немного анимации и два различных типа: default - по умолчанию и black - более темный вариант, который будет использоваться как плейсхолдер для заголовков.

.rect { animation: reсt-pulse 1.5s infinite; }
.rect_type_default { background: #e6e6e6; }
.rect_type_black { background: #aaa; }

@keyframes reсt-pulse {
  0% { opacity: .6; }
  50% { opacity: 1; }
  100% { opacity: .6;}
}

По сути, это все, что нужно. Мы на своих проектах используем подход, в котором для каждого компонента кладем рядом компонент заглушку (*.stub.ts):

Компонент заглушка выглядит подобным образом (результатом будет скриншот в начале статьи):

const ComponentStub: StatelessComponent = () => {
  return (
    <div className="stub-container">
      <Rect height="50px" type="black" />
      <Rect height="18px" width="200px" />
      <Rect height="18px" />
      <Rect height="18px" />
      <Rect height="18px" />
      <Rect height="18px" />
      <Rect height="18px" />
      <Rect height="18px" />
    </div>
  );
}

И стили для контейнера:

.stub-container {
  display: flex;
  flex-direction: column;
}

.stub-container > .rect {
  margin: 5px;
}

В основном компоненте в пропсах приходит параметр isLoading, по которому и отрисовываем content placeholder. Поиграться можете в онлайн песочнице codesandbox.

render() {
    const { isLoading } = this.props;

    if (isLoading) {
        return <ComponentStub />;
    }

    return (...);
}

Хинт: Если нужно отрендерить несколько одинаковых карточек, то можно в компонент-заглушку передавать свойсто itemsCount, а внутри создавать массив с использованием Array.fill и с помощью map рендерить нужное количество заглушек:

<ComponentStub itemsCount={5} />
/* ... */
render() {
    const items = Array(this.props.itemsCount).fill(0);

    return items.map(...)
}

Основная сложность в том, чтобы заглушки были похожи на основной контент. Когда то видел даже онлайн решения, которые по скриншоту могут создать разметку для плейсхолдеров или подобрать высоту и отступы, но к сожалению, сейчас не нашел ни одного рабочего варианта. Так же вы можете посмотреть в сторону готовой библиотеки buildo/react-placeholder, у которой куча разных возможностей или zalog/placeholder-loading.

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

На сегодня все, до связи!

Оставайтесь на связи
Чтобы не пропустить новые посты или анонсы проектов, которые я делаю, вы можете присоединиться к телеграм каналу. А так же оставаться на связи, задавать вопросы или просто вместе обсуждать различные инженерные темы.
Присоединиться

Вам может быть интересно

Почему я не использую NextJS

14/10/2022
О каких технологиях вы задумываетесь, когда речь заходит про SSR и React?
125

Архитектура фронтенда на основе вертикальных слайсов

28/05/2023
Обсудим подход вертикальных слайсов через технические слои во фронтенде
137

История одной оптимизации React приложения

29/03/2020
Рассмотрим как оптимизировать React приложения на реальном примере
16