Ленивая загрузка

Ленивая загрузка (lazy loading) — это подход к подключению и инициализации ресурсов только в тот момент, когда они действительно необходимы. В случае с библиотекой iziToast это означает, что её JavaScript и CSS не загружаются при первоначальной загрузке страницы, а подгружаются динамически при первом вызове уведомления.

Такой подход особенно важен для оптимизации производительности:

  • уменьшает начальный размер загружаемых ресурсов
  • ускоряет первый рендер страницы
  • снижает нагрузку на сеть и браузер

Базовая схема ленивой загрузки iziToast

Ленивая загрузка включает три этапа:

  1. Проверка наличия библиотеки в текущем контексте
  2. Динамическая загрузка CSS и JS
  3. Выполнение целевого действия (показ уведомления)

Простейшая реализация:

function loadIziToast() {
    return new Promise((resolve, reject) => {
        if (window.iziToast) {
            resolve();
            return;
        }

        // Подключение CSS
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = 'https://cdn.jsdelivr.net/npm/izitoast/dist/css/iziToast.min.css';
        document.head.appendChild(link);

        // Подключение JS
        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/izitoast/dist/js/iziToast.min.js';

        script.onl oad = () => resolve();
        script.oner ror = () => reject(new Error('Ошибка загрузки iziToast'));

        document.body.appendChild(script);
    });
}

Обёртка для вызова уведомлений

Чтобы скрыть логику загрузки и упростить использование, создаётся универсальная функция:

async function showToast(options) {
    await loadIziToast();
    iziToast.show(options);
}

Пример использования:

showToast({
    title: 'Готово',
    message: 'Данные успешно сохранены',
    color: 'green'
});

Кэширование загрузки

Без дополнительной оптимизации при каждом вызове будет происходить проверка и возможная повторная инициализация. Для предотвращения этого используется кэширование состояния загрузки:

let iziToastPromise = null;

function loadIziToast() {
    if (window.iziToast) {
        return Promise.resolve();
    }

    if (iziToastPromise) {
        return iziToastPromise;
    }

    iziToastPromise = new Promise((resolve, reject) => {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = 'https://cdn.jsdelivr.net/npm/izitoast/dist/css/iziToast.min.css';
        document.head.appendChild(link);

        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/izitoast/dist/js/iziToast.min.js';

        script.onl oad = () => resolve();
        script.oner ror = reject;

        document.body.appendChild(script);
    });

    return iziToastPromise;
}

Это гарантирует:

  • единичную загрузку
  • повторное использование уже существующего Promise
  • отсутствие гонок при параллельных вызовах

Разделение логики уведомлений

Для удобства и читаемости кода создаются специализированные функции:

async function toastSuccess(message) {
    await loadIziToast();
    iziToast.success({
        title: 'OK',
        message
    });
}

async function toastError(message) {
    await loadIziToast();
    iziToast.error({
        title: 'Ошибка',
        message
    });
}

Ленивое подключение через динамический import

При использовании сборщиков (Webpack, Vite) предпочтительно использовать динамический import:

async function loadIziToast() {
    if (window.iziToast) return;

    const iziToast = await import('izitoast');
    await import('izitoast/dist/css/iziToast.min.css');

    window.iziToast = iziToast.default;
}

Преимущества:

  • автоматическое разделение кода (code splitting)
  • оптимизация загрузки чанков
  • управление зависимостями на уровне сборщика

Отложенная загрузка по событию

Часто уведомления используются только после взаимодействия пользователя. В этом случае загрузка может быть инициирована заранее:

document.addEventListener('click', () => {
    loadIziToast();
}, { once: true });

Это позволяет:

  • скрыть задержку при первом показе уведомления
  • загрузить библиотеку в момент, когда пользователь уже активен

Обработка ошибок загрузки

Важно учитывать возможные сбои:

async function safeToast(options) {
    try {
        await loadIziToast();
        iziToast.show(options);
    } catch (e) {
        console.error('Не удалось загрузить iziToast', e);
    }
}

Предзагрузка ресурсов

Иногда используется компромисс между полной ленивой загрузкой и мгновенным показом:

<link rel="preload" href="https://cdn.jsdelivr.net/npm/izitoast/dist/js/iziToast.min.js" as="script">
<link rel="preload" href="https://cdn.jsdelivr.net/npm/izitoast/dist/css/iziToast.min.css" as="style">

Это ускоряет последующую загрузку без немедленного выполнения.


Оптимизация CSS

CSS можно подключать отдельно и заранее:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/izitoast/dist/css/iziToast.min.css">

А JavaScript загружать лениво. Это устраняет “мигание” стилей при первом показе уведомления.


Интеграция с SPA (Single Page Application)

В SPA ленивую загрузку удобно реализовать через сервис уведомлений:

class ToastService {
    static async init() {
        if (!this.ready) {
            await loadIziToast();
            this.ready = true;
        }
    }

    static async show(options) {
        await this.init();
        iziToast.show(options);
    }
}

Использование:

ToastService.show({
    message: 'Маршрут изменён'
});

Поведенческие особенности

При ленивой загрузке важно учитывать:

  • Задержка первого вызова — из-за загрузки скрипта
  • Параллельные вызовы — требуют синхронизации (Promise)
  • Повторное использование — библиотека должна сохраняться в глобальном контексте

Когда применять ленивую загрузку

Подход оправдан в случаях:

  • уведомления используются редко
  • важна скорость первого рендера
  • проект ориентирован на мобильные устройства
  • требуется минимизация bundle size

Менее эффективен:

  • при частом использовании уведомлений
  • в критически быстрых интерфейсах (где задержка недопустима)

Итоговая архитектура решения

Оптимальная структура:

  • loader.js — отвечает за загрузку iziToast
  • toastService.js — API для показа уведомлений
  • основной код — использует сервис без знания о загрузке

Такой подход обеспечивает:

  • модульность
  • повторное использование
  • чистую архитектуру
  • прозрачность логики загрузки