DevGang
Авторизоваться

Генерация оптимизированных форматов изображений с помощью 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>, для создания различных версий одного и того же изображения и отображения лучшей для каждого устройства. Благодаря этому наш сайт будет работать быстрее и выглядеть лучше для всех, кто им пользуется!

Источник:

#JavaScript #NodeJS #React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться