Как улучшить производительность фронтенда с помощью Lazy Loading и Code Splitting

8 минут чтения
Средний рейтинг статьи — 4.8

В современных веб-приложениях производительность фронтенда играет ключевую роль в улучшении пользовательского опыта. Одними из наиболее эффективных методов улучшения времени загрузки страниц являются ленивое (lazy) загрузка и разделение (code splitting) кода. Давайте рассмотрим, как эти техники работают и как их можно внедрить в ваш проект.

Что такое ленивое (lazy) загрузка?

Ленивая загрузка – это техника, при которой ресурсы (например, изображения, скрипты или компоненты) загружаются только тогда, когда они становятся необходимыми. Это позволяет сократить время первоначальной загрузки страницы и уменьшить нагрузку на сеть.

Преимущества ленивой загрузки

  1. Сокращение времени загрузки: Только необходимые ресурсы загружаются сразу, что ускоряет загрузку страницы.
  2. Экономия трафика: Меньше данных передается по сети, что особенно важно для пользователей с медленным интернет-соединением.
  3. Улучшение пользовательского опыта: Пользователи видят основной контент быстрее, что делает взаимодействие с сайтом более приятным.

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

Рассмотрим пример ленивой загрузки изображений с использованием Intersection Observer API:

document.addEventListener("DOMContentLoaded", function() {
    // Находим все изображения с классом "lazy"
    const lazyImages = document.querySelectorAll("img.lazy");
 
    // Проверяем, поддерживается ли Intersection Observer
    if ("IntersectionObserver" in window) {
        // Создаем новый наблюдатель Intersection Observer
        let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
            entries.forEach(function(entry) {
                // Если изображение пересекается с областью видимости
                if (entry.isIntersecting) {
                    let lazyImage = entry.target;
                    // Заменяем src изображения на значение из data-src
                    lazyImage.src = lazyImage.dataset.src;
                    // Удаляем класс "lazy"
                    lazyImage.classList.remove("lazy");
                    // Прекращаем наблюдение за этим изображением
                    lazyImageObserver.unobserve(lazyImage);
                }
            });
        });
 
        // Начинаем наблюдение за каждым ленивым изображением
        lazyImages.forEach(function(lazyImage) {
            lazyImageObserver.observe(lazyImage);
        });
    } else {
        // Фоллбек для браузеров, не поддерживающих Intersection Observer
        let lazyLoad = function() {
            let active = false;
 
            if (active === false) {
                active = true;
 
                setTimeout(function() {
                    lazyImages.forEach(function(lazyImage) {
                        // Проверяем, что изображение находится в области видимости и отображается
                        if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
                            // Заменяем src изображения на значение из data-src
                            lazyImage.src = lazyImage.dataset.src;
                            // Удаляем класс "lazy"
                            lazyImage.classList.remove("lazy");
 
                            // Убираем это изображение из списка наблюдаемых
                            lazyImages = lazyImages.filter(function(image) {
                                return image !== lazyImage;
                            });
 
                            // Если все изображения загружены, убираем слушатели событий
                            if (lazyImages.length === 0) {
                                document.removeEventListener("scroll", lazyLoad);
                                window.removeEventListener("resize", lazyLoad);
                                window.removeEventListener("orientationchange", lazyLoad);
                            }
                        }
                    });
 
                    active = false;
                }, 200);
            }
        };
 
        // Добавляем слушатели событий для загрузки изображений при прокрутке, изменении размера окна или изменении ориентации устройства
        document.addEventListener("scroll", lazyLoad);
        window.addEventListener("resize", lazyLoad);
        window.addEventListener("orientationchange", lazyLoad);
    }
});

Что происходит в этом коде?

  1. Поиск ленивых изображений: Сначала скрипт находит все изображения с классом lazy на странице.
  2. Проверка поддержки Intersection Observer: Если браузер поддерживает Intersection Observer, создается наблюдатель lazyImageObserver.
  3. Наблюдение за изображениями: Наблюдатель следит за пересечением изображений с областью видимости. Как только изображение попадает в область видимости, его src обновляется на значение из data-src, и класс lazy удаляется. Наблюдение за этим изображением прекращается.
  4. Фоллбек для неподдерживающих браузеров: Если Intersection Observer не поддерживается, создается функция lazyLoad, которая проверяет положение каждого изображения относительно области видимости при прокрутке, изменении размера окна или изменении ориентации устройства. Если изображение попадает в область видимости, его src обновляется, и оно удаляется из списка наблюдаемых изображений. Если все изображения загружены, слушатели событий удаляются.

Что такое разделение (code splitting) кода?

Разделение кода – это техника, при которой код разбивается на небольшие части (чанки), которые загружаются по мере необходимости. Это позволяет уменьшить размер загружаемого файла и ускорить время загрузки страницы.

Преимущества разделения кода

  1. Уменьшение размера бандла: Меньшие чанки загружаются быстрее, чем один большой файл.
  2. Оптимизация использования кэша: Если код изменяется, только измененные чанки нужно перезагрузить, что улучшает производительность.
  3. Асинхронная загрузка: Чанки могут загружаться асинхронно, что сокращает время загрузки страницы.

Реализация разделения кода

Рассмотрим пример использования Webpack для разделения кода:

  1. Установите Webpack и необходимые зависимости:
npm install --save-dev webpack webpack-cli webpack-bundle-analyzer
  1. Настройте Webpack в webpack.config.js:
const path = require('path');
 
module.exports = {
    entry: {
        main: './src/index.js',
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
    optimization: {
        splitChunks: {
            chunks: 'all',
        },
    },
};
  1. Используйте динамический импорт в вашем коде:
// index.js
import('./moduleA').then(moduleA => {
    moduleA.doSomething();
});
 
import('./moduleB').then(moduleB => {
    moduleB.doSomethingElse();
});

Заключение

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

8 минут чтения
Средний рейтинг статьи — 4.8

Настроить мониторинг за 30 секунд

Надежные оповещения о даунтаймах. Без ложных срабатываний