Генерация оптимизированных форматов изображений с помощью Node.js
Изображения являются важной частью любого веб-приложения, однако при неправильной оптимизации они могут стать основным источником проблем с производительностью. В этой статье мы рассмотрим, как с помощью Node.js и React автоматически генерировать оптимизированные форматы изображений и отображать их в оптимальном для браузера пользователя формате.
Настройка
Для начала нам нужна библиотека, которая будет заниматься обработкой изображений, и я выбрал именно sharp
.
npm i sharp
Sharp - это высокопроизводительная библиотека Node.js для обработки и манипулирования изображениями. Она разработана с учетом быстродействия и экономии памяти, что делает ее идеальной для обработки больших изображений и генерации изображений в различных форматах.
Сценарий генерации
Первым шагом в оптимизации изображений для сайта является создание нескольких форматов каждого изображения, каждый из которых имеет свои преимущества и недостатки. Одни форматы, например JPEG, подходят для сложных изображений с большим количеством цветов, другие, например WebP, - для более простых изображений с меньшим количеством цветов.
Для генерации различных форматов изображений мы можем использовать Node.js и библиотеку обработки изображений Sharp. Приведем пример скрипта, который генерирует форматы avif и webp для каждого изображения в папке images:
const sharp = require('sharp');
const fs = require('fs');
const inputFolder = 'images';
const outputFolder = 'output';
const formats = ['avif', 'webp'];
if (!fs.existsSync(outputFolder)) {
fs.mkdirSync(outputFolder);
}
fs.readdir(inputFolder, (err, files) => {
if (err) {
console.error(err);
return;
}
files.forEach(file => {
if (file.endsWith('.jpg') || file.endsWith('.jpeg') || file.endsWith('.png')) {
const inputPath = `${inputFolder}/${file}`;
const name = file.substring(0, file.lastIndexOf('.'));
formats.forEach(format => {
const outputPath = `${outputFolder}/${name}.${format}`;
if (!fs.existsSync(outputPath)) {
sharp(inputPath)
.toFormat(format, { quality: 80 })
.toFile(outputPath, (err) => {
if (err) {
console.error(err);
} else {
console.log(`${name}.${format} saved`);
}
});
}
});
}
});
});
Пояснение:
const sharp = require('sharp');
const fs = require('fs');
const inputFolder = 'images';
const outputFolder = 'output';
const formats = ['avif', 'webp'];
В этих строках скрипт импортирует библиотеки sharp
и fs
, задает папку ввода - images
, папку вывода - output
, а также определяет форматы для генерации - avif
и webp
.
if (!fs.existsSync(outputFolder)) {
fs.mkdirSync(outputFolder);
}
Здесь скрипт проверяет, существует ли папка outputFolder
, и, если её нет, создает её с помощью функции fs.mkdirSync()
. Это гарантирует существование выходной папки до генерации изображений.
fs.readdir(inputFolder, (err, files) => {
if (err) {
console.error(err);
return;
}
Этот код считывает содержимое папки inputFolder
с помощью функции fs.readdir()
. При возникновении ошибки он записывает её в консоль и возвращает.
files.forEach(file => {
if (file.endsWith('.jpg') || file.endsWith('.jpeg') || file.endsWith('.png')) {
В этом коде выполняется циклический просмотр каждого файла в папке inputFolder
с помощью функции files.forEach()
. Если имя файла заканчивается на .jpg
, .jpeg
или .png
, то выполняется генерация соответствующих avif
- и webp
-файлов.
const inputPath = `${inputFolder}/${file}`;
const name = file.substring(0, file.lastIndexOf('.'));
Здесь сценарий определяет путь к входному файлу как inputPath
и извлекает имя файла без расширения, которое будет использоваться в качестве имени выходного файла.
formats.forEach(format => {
const outputPath = `${outputFolder}/${name}.${format}`;
if (!fs.existsSync(outputPath)) {
sharp(inputPath)
.toFormat(format, { quality: 80 })
.toFile(outputPath, (err) => {
if (err) {
console.error(err);
} else {
console.log(`${name}.${format} saved`);
}
});
}
});
Здесь скрипт перебирает каждый формат (т.е. avif
и webp
) с помощью функции formats.forEach()
. Для каждого формата он определяет путь к выходному файлу как outputPath
.
Если выходной файл еще не существует, то с помощью функции Sharp toFormat()
формируется соответствующее изображение в заданном формате с качеством 80. Далее с помощью функции toFile()
выполняется сохранение выходного файла, а в консоль выводится сообщение о том, что файл сохранен.
Отображение оптимизированных изображений в React
После того как мы сгенерировали несколько оптимизированных форматов изображений для каждого входного изображения, мы можем отобразить их в нашем React-приложении. Для этого мы можем использовать элементы HTML <picture>
и <source>
, чтобы указать различные источники изображений для разных форматов. Приведем пример компонента React, который принимает имя изображения в качестве параметра и отображает его в наилучшем формате для браузера пользователя:
import React from 'react';
const Image = ({ name }) => {
const avifSrc = `/images/${name}.avif`;
const webpSrc = `/images/${name}.webp`;
const jpgSrc = `/images/${name}.jpg`;
return (
<picture>
<source srcSet={avifSrc} type="image/avif" />
<source srcSet={webpSrc} type="image/webp" />
<img src={jpgSrc} alt={name} />
</picture>
);
};
export default Image;
Этот код определяет три различных URL-адреса источников изображений в зависимости от переданного реквизита name
:
avifSrc
соответствует формату изображенияavif
.webpSrc
соответствует формату изображенияwebp
.jpgSrc
соответствует стандартному формату изображенияjpg
, который будет использоваться в качестве запасного варианта для браузеров, не поддерживающихavif
илиwebp
.
return (
<picture>
<source srcSet={avifSrc} type="image/avif" />
<source srcSet={webpSrc} type="image/webp" />
<img src={jpgSrc} alt={name} />
</picture>
);
};
Здесь скрипт возвращает элемент <picture>
, который отображает изображение в наилучшем формате для браузера пользователя, исходя из имеющихся форматов. Внутри элемента <picture>
находятся два элемента <source>
, один для avif
, другой для webp
. Эти элементы задают различные источники изображений для разных форматов с помощью атрибута srcSet
и атрибута type
, указывающего на MIME-тип каждого формата.
Наконец, существует элемент резервного копирования <img>
, который отображает изображение в стандартном формате jpg
для браузеров, не поддерживающих avif
или webp
. Этот элемент использует атрибут src
для указания источника изображения и атрибут alt
для предоставления альтернативного текста для изображения.
Заключение
Изображения на сайтах могут загружаться медленно и не всегда хорошо выглядеть на различных устройствах. Важно сделать так, чтобы они загружались быстрее и выглядели лучше, чтобы люди могли больше наслаждаться вашим сайтом. Мы научились использовать специальные инструменты, такие как Sharp и HTML <picture>
и <source>
, для создания различных версий одного и того же изображения и отображения лучшей для каждого устройства. Благодаря этому наш сайт будет работать быстрее и выглядеть лучше для всех, кто им пользуется!