Как ускорить сайт с помощью ленивой загрузки изображений

2 года назад·1 мин. на чтение

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

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

Что такое ленивая загрузка?

Ленивая загрузка (отложенная загрузка) — это метод, используемый для отсрочки загрузки контента до тех пор, пока он не понадобится. В случае изображений это означает, что изображение не будет загружено до тех пор, пока пользователь не прокрутит до точки, где изображение будет видно на экране. Это отличный способ ускорить работу вашего сайта, поскольку вы загружаете только те изображения, которые пользователь действительно увидит. Это особенно полезно для сайтов с большим количеством изображений, поскольку вы можете сэкономить много пропускной способности, загружая только те изображения, которые пользователь действительно увидит. Если у вас высокая скорость интернета или вы просматриваете сайты только с небольшими, хорошо оптимизированными изображениями, вы можете не увидеть преимущества отложенной загрузки изображений, поскольку вы можете загрузить все изображения почти мгновенно. Но для всех остальных ленивая загрузка изображений меняет играет важную роль. Это касается не только людей со сверхмедленным интернет-соединением. Изображения являются одним из, если не самым большим по размеру контентом, который загрузит ваш пользователь, поэтому, даже если у него быстрое подключение к Интернету, ленивая загрузка изображений все равно может иметь огромное значение для времени загрузки вашего сайта.

Базовая реализация ленивой загрузки

Как я уже упоминал в начале этой статьи, ленивая загрузка изображений так же проста, как добавление одного атрибута к тегу изображения. Атрибуту loading можно присвоить значение lazy, чтобы включить отложенную загрузку изображения. Браузер автоматически определит, когда загружать изображение, в зависимости от того, насколько близко изображение находится на экране.
<img src="image.jpg" loading="lazy" />
Самым большим недостатком этой базовой отложенной загрузки является то, что пользователь увидит пустое место, где должно быть изображение, пока изображение не будет загружено. Это не идеальный пользовательский опыт, поэтому оставшаяся часть этой статьи покажет вам, как воспользоваться отложенной загрузкой, чтобы показать размытое изображение-заполнитель до тех пор, пока не будет загружено полное изображение.

Продвинутая отложенная загрузка

Размытые изображения-заполнители отображаются до тех пор, пока не будет загружено полное изображение, и являются первым шагом к созданию этого расширенного эффекта отложенной загрузки. Чтобы создать размытое изображение-заполнитель, вам просто нужно создать версию изображения со сверхнизким разрешением. Есть много способов сделать это, например, использовать такой сервис, как BlurHash, вручную изменить размер изображения в таком инструменте, как Figma, или автоматически с помощью такого инструмента, как ffmpeg. Мы будем использовать ffmpeg для создания изображений-заполнителей для этой статьи, поскольку это наиболее гибкий вариант, который можно легко автоматизировать. Все, что нужно сделать, это запустить приведенный ниже код в командной строке в каталоге, содержащем изображение, для которого требуется сгенерировать изображение-заполнитель.
ffmpeg -i imageName.jpg -vf scale=20:-1 imageName-small.jpg
При этом будет сгенерировано изображение шириной 20 пикселей, а высота будет автоматически рассчитана для сохранения пропорций исходного изображения. Вы можете изменить ширину на любую другую, но по наблюдениям, 20 пикселей хорошо подходят для большинства изображений и достаточно малы, чтобы загружаться почти мгновенно даже при медленном интернет-соединении. Изображения-заполнители будут примерно по 1КБ каждое.
Следующим шагом является создание div и установка фонового изображения этого div на наше супер маленькое изображение. Это будет изображение-заполнитель, которое будет отображаться до тех пор, пока не будет загружено полное изображение. Наш код будет выглядеть примерно так.
<div class="blurred-img"></div>
.blurred-img {
  background-image: url(imageName-small.jpg);
  background-repeat: no-repeat;
  background-size: cover;
}
Этот div с blurred-img имеет размер в зависимости от размера содержимого в нем. Однако мы можем легко исправить это, добавив img в наш div и убедившись, что он скрыт по умолчанию, чтобы мы никогда не видели его в наполовину загруженном состоянии.
<div class="blurred-img">
  <img src="imageName.jpg" loading="lazy" />
</div>
.blurred-img img {
  opacity: 0;
}
Это даст нам эффект, который мы ищем. Эффект размытия, который мы получаем автоматически, связан с тем, что сверхмаленькое изображение автоматически увеличивается браузером. Если вы хотите добавить больше размытия, вы всегда можете использовать свойство CSS filter, чтобы добавить фильтр к blurred-img.
.blurred-img {
  filter: blur(10px);
}
Вы даже можете сделать еще один шаг вперед, добавив пульсирующую анимацию к изображению-заполнителю. Это сделает еще более очевидным, что изображение загружается.
.blurred-img::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  animation: pulse 2.5s infinite;
  background-color: white;
}

@keyframes pulse {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 0.1;
  }
  100% {
    opacity: 0;
  }
}
Теперь единственное, что осталось сделать, это показать основное изображение после его загрузки. Это немного сложнее, чем остальная часть кода, который мы написали до сих пор, поскольку требует от нас использования JavaScript, но все же довольно просто. Нам просто нужно добавить прослушиватель событий к изображению, который будет срабатывать после загрузки изображения.
<div class="blurred-img">
  <img src="imageName.jpg" loading="lazy" />
</div>
const blurredImageDiv = document.querySelector(".blurred-image")
const img = blurredImageDiv.querySelector("img")
function loaded() {
  blurredImageDiv.classList.add("loaded")
}

if (img.complete) {
  loaded()
} else {
  img.addEventListener("load", loaded)
}
.blurred-img {
  background-repeat: no-repeat;
  background-size: cover;
}
.blurred-img::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  animation: pulse 2.5s infinite;
  background-color: var(--text-color);
}

@keyframes pulse {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 0.1;
  }
  100% {
    opacity: 0;
  }
}

.blurred-img.loaded::before {
  animation: none;
  content: none;
}

.blurred-img img {
  opacity: 0;
  transition: opacity 250ms ease-in-out;
}

.blurred-img.loaded img {
  opacity: 1;
}
Здесь много кода, поэтому разберем его шаг за шагом. В коде JavaScript мы выбираем blurred-img а затем выбираем img в этом div. Затем мы проверяем свойство complete у img, чтобы увидеть, загрузился ли он еще. Если это так, это означает, что изображение уже загружено, поэтому мы можем просто вызвать функцию loaded. Однако, если это условие ложно, нам нужно добавить прослушиватель событий в img, который будет срабатывать после загрузки изображения, а затем вызывать loaded. loaded просто добавляет класс loaded в blurred-img. В CSS у нас есть несколько изменений в коде. Сначала мы удалили animation/content из элемента blurred-img::before. Это остановит пульсирующую анимацию после загрузки изображения. Мы также добавили transition к элементу img, чтобы он плавно исчезал при добавлении loaded класса в div blurred-img.img Наконец, мы изменяем непрозрачность img на 1, чтобы она была видна при загрузке.

Итоги

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

Полное руководство по React Router v6. Часть 3 - Управление навигацией

2 года назад·5 мин. на чтение

В этой статье рассмотрим, как перемещаться между роутами. Как добавить ссылки, как перейти по ссылке программно, как передать данные при переходе.

Серия статей о React Router v6 состоит из 4 частей.
  1. Основы React Router
  2. Продвинутые определения маршрутов
  3. Управление навигацией (рассматривается в этой статье)
  4. Подробно о роутерах
В этой статье рассмотрим следующие темы.
  1. Навигация по ссылкам
  2. Ручная навигация
  3. Передача данных при навигации

Навигация по ссылкам

Навигация по ссылкам - это самая простая и распространенная форма навигации. Мы уже видели самую простую форму навигации по ссылкам с помощью компонента Link.
<Link to="/">Home</Link>
<Link to="/books">Books</Link>
Однако эти компоненты Link могут стать немного сложнее. Например, у вас могут быть абсолютные ссылки, подобные приведенным выше ссылкам, или у вас могут быть ссылки, относящиеся к текущему отображаемому компоненту.
<Link to="/">Home</Link>
<Link to="../">Back</Link>
<Link to="edit">Edit</Link>
Например, представьте, что мы находимся в маршруте /books/3 с приведенными выше ссылками. Первая ссылка приведет к / маршруту, так как это абсолютный маршрут. Любой маршрут, начинающийся с /, является абсолютным маршрутом. Вторая ссылка приведет к маршруту /books, так как это относительная ссылка, которая поднимается на один уровень вверх от /books/3 к /books. Наконец, наша третья ссылка перейдет на страницу /books/3/edit, так как она добавит путь к пропсу to в конец текущей ссылки, поскольку это относительная ссылка.
Помимо пропса to, есть также 3 других пропса, которые важны для компонента Link.

replace

Проп replace имеет логическое значение, которое, если установить в true, приведет к тому, что эта ссылка заменит текущую страницу в истории браузера. Представьте, что у вас есть следующая история браузера.
/
/books
/books/3
Если вы нажмете на ссылку, ведущую на страницу /books/3/edit, но у нее проп replace имеет значение `trueё, ваша новая история будет выглядеть следующим образом.
/
/books
/books/3/edit
Страница, на которой вы находились в данный момент, была заменена новой страницей. Это означает, что если вы нажмете кнопку «Назад» на новой странице, вы вернетесь на страницу /books, а не на страницу /books/3.

reloadDocument

Проп reloadDocument также является логическим. Если задано значение true, компонент Link будет действовать как обычный тег <a> и выполнять полное обновление страницы при навигации вместо того, чтобы просто повторно отображать содержимое внутри компонента Routes.

state

Финальный проп называется state. Этот проп позволяет передавать данные вместе с Link, которая не отображается нигде в URL-адресе. Рассмотрим эту возможность более подробно, когда будем говорить о навигационных данных.

NavLink

Компонент NavLink работает точно так же, как компонент Link, но он предназначен специально для отображения активных состояний ссылок, например, в панелях навигации. По умолчанию, если проп to у NavLink совпадает с URL-адресом текущей страницы, к ссылке будет добавлен класс active, который можно использовать для стилизации. Если этого недостаточно, можно передать функцию с параметром isActive в className, или в свойства style, или в качестве дочерних элементов NavLink.
<NavLink
  to="/"
  style={({ isActive }) => ({ color: isActive ? "red" : "black" })}
>
  Home
</NavLink>
Еще NavLink имеет проп end, который используется для помощи во вложенной маршрутизации. Например, если мы находимся на странице /books/3, это означает, что мы визуализируем компонент Book, который вложен в наш маршрут /books. Это означает, что если у нас есть NavLink с to установленным в /books, он будет считаться активным. Это связано с тем, что NavLink считается активным, если URL-адрес to совпадает с пропсом to NavLink или если текущий Route находится внутри родительского компонента c path, который совпадает с пропсом to компонента NavLink. Если вы не хотите такое поведение по умолчанию, вы можете установить проп end в true, чтобы URL-адрес страницы точно соответствовал пропсу to NavLink.

Ручная навигация

Иногда нужно вручную перемещаться на основе таких вещей, как отправка формы или отсутствие доступа к определенной странице. Для таких случаев можно использовать либо компонент Navigate, либо хук useNavigation.

Компонент Navigate

Компонент Navigate — это очень простой компонент, который при визуализации автоматически перенаправляет пользователя на значение пропса to компонента.
<Navigate to="/" />
Компонент Navigate имеет те же пропсы, что и компонент Link, таким образом вы можете передать ему to, replace и state.

Хук useNavigation

Хук useNavigation представляет собой хук, который не принимает никаких параметров и возвращает одну функцию navigate, которую вы можете использовать для перенаправления пользователя на определенные страницы. Эта функция navigate принимает два параметра. Первый параметр — это местоположение to, в которое вы хотите перенаправить пользователя, а второй параметр — это объект, который может иметь ключи для replace и state.
const navigate = useNavigate()

function onSubmit() {
  // Отправка значения формы
  navigate("/books", { replace: true, state: { bookName: "Fake Title" }})
}
Приведенный выше код перенаправит пользователя на маршрут /books. Он также заменит текущий маршрут в истории и передаст некоторую информацию через state. Другой способ, которым вы можете использовать функцию navigate, — передать ей число. Это позволит вам имитировать нажатие кнопки вперед/назад.
navigate(-1) // Перемещает назад на одну страницу в истории
navigate(-3) // Перемещает назад на три страницу в истории
navigate(1) // Перемещает вперед на одну страницу в истории

Передача данных при навигации

Наконец, пришло время поговорить о передаче данных между страницами. Существует 3 основных способа передачи данных между страницами.
  1. Динамические параметры
  2. Параметры поиска
  3. Данные состояния/местоположения

Динамические параметры

Мы уже говорили о том, как использовать динамические параметры в URL-адресах с помощью хука useParams. Это лучший способ обработки передаваемой информации, такой как идентификаторы.

Параметры поиска

Параметры поиска — это все параметры, которые идут после ? в URL-адресе (?name=Kyle&age=27). Для работы с параметрами поиска необходимо использовать хук useSearchParams, который работает очень похоже на useState.
import { useSearchParams } from "react-router-dom"

export function SearchExample() {
  const [searchParams, setSearchParams] = useSearchParams({ n: 3 })
  const number = searchParams.get("n")

  return (
    <>
      <h1>{number}</h1>
      <input
        type="number"
        value={number}
        onChange={e => setSearchParams({ n: e.target.value })}
      />
    </>
  )
}
В этом примере у нас есть инпут, который по мере ввода будет обновлять поисковую часть нашего URL-адреса. Например, если наш ввод имеет значение 32, наш URL-адрес будет выглядеть так: http://localhost:3000?n=32. Хук useSearchParams принимает начальное значение, как useState, и в нашем случае наше начальное значение n равно 3. Затем этот хук возвращает два значения. Первое значение - это все наши параметры поиска, а второе значение - это функция для обновления наших параметров поиска. Функция set просто принимает один аргумент, который является новым значением параметров поиска. Однако первое значение, содержащее параметры поиска, немного более запутанно. Это связано с тем, что это значение относится к типу URLSearchParams. Вот почему нам нужно использовать синтаксис .get в строке 5 выше.

Данные состояния/местоположения

Вся эта информация в этом случае будет доступна через хук useLocation. Использовать этот хук очень просто, так как он возвращает одно значение и не принимает никаких параметров.
const location = useLocation()
Если у нас есть следующий URL http://localhost/books?n=32#id то возвращаемое значение useLocation будет выглядеть следующим образом.
{
  pathname: "/books",
  search: "?n=32",
  hash: "#id",
  key: "2JH3G3S",
  state: null
}
Этот объект местоположения содержит всю информацию, относящуюся к нашему URL-адресу. Он также содержит уникальный ключ, который можно использовать для кэширования, если вы хотите кэшировать информацию, когда пользователь нажимает кнопку «Назад», чтобы вернуться на страницу. Вы также заметите, что у нас есть проп state, возвращаемое из useLocation. Эти данные состояния могут быть любыми и передаются между страницами без сохранения в URL-адресе. Например, если вы нажмете на Link, которая выглядит следующим образом:
<Link to="/books" state={{ name: "Kyle" }}>
тогда значение состояния в объекте location будет установлено в { name: "Kyle" }. Это может быть очень полезно, если, например, вы хотите отправлять простые сообщения между страницами, которые не должны храниться в URL-адресе. Хорошим примером этого может быть что-то вроде сообщения об успешном завершении, которое отправляется на страницу, на которую вы перенаправляетесь после создания новой книги.