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

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


Основы подключения контроллеров

Stimulus работает на основе HTML-атрибутов data-controller, связывая элементы DOM с соответствующими JavaScript-классами. В обычной схеме все контроллеры импортируются и регистрируются заранее:

import { Application } from "@hotwired/stimulus"
import ExampleController from "./controllers/example_controller"

const application = Application.start()
application.register("example", ExampleController)

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


Подход с динамическим импортом

Stimulus поддерживает динамический импорт контроллеров с использованием стандартной функции import(). Это позволяет регистрировать контроллер только тогда, когда элемент появляется на странице:

import { Application } from "@hotwired/stimulus"

const application = Application.start()

document.addEventListener("DOMContentLoaded", () => {
  document.querySelectorAll("[data-controller]").forEach(async element => {
    const controllerName = element.getAttribute("data-controller")
    const module = await import(`./controllers/${controllerName}_controller.js`)
    application.register(controllerName, module.default)
  })
})

Ключевые моменты:

  • import() возвращает промис, поэтому регистрация контроллера происходит асинхронно.
  • Имя файла контроллера должно соответствовать значению data-controller с добавлением _controller.js.
  • Такой подход снижает начальную нагрузку на страницу, особенно при большом количестве контроллеров.

Использование data-action для ленивой инициализации

Можно комбинировать ленивую загрузку с событием, чтобы контроллер загружался только при взаимодействии с пользователем:

<button data-controller="lazy" data-action="click->lazy#load">Нажми меня</button>
const application = Application.start()

application.register("lazy", class {
  async load(event) {
    const module = await import("./controllers/real_controller.js")
    application.register("real", module.default)
    const controller = application.getControllerForElementAndIdentifier(event.target, "real")
    controller.initialize()
  }
})

В этом примере контроллер real загружается только при клике на кнопку, что полностью исключает его загрузку при инициализации страницы.


Автоматизация с stimulus-loading

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

import { Application } from "@hotwired/stimulus"
import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers"

const application = Application.start()
const context = require.context("./controllers", true, /\.js$/)
application.load(definitionsFromContext(context))

Здесь Webpack автоматически создаёт контекст для всех контроллеров, а их регистрация происходит по мере первого использования.


Особенности и ограничения

  • Асинхронная природа: Ленивая загрузка делает контроллер доступным только после завершения промиса import(). Для элементов, которые требуют немедленной реакции, этот подход может потребовать дополнительных обработчиков.
  • Кэширование модулей: После первого импорта контроллеры остаются в кэше браузера, поэтому последующие вызовы будут синхронными.
  • Совместимость с Turbo и Hotwire: Динамическая регистрация контроллеров полностью совместима с Turbo Frames и Turbo Streams, что позволяет загружать контроллер только для конкретного фрейма.

Оптимизация производительности

Для максимальной эффективности стоит комбинировать ленивую загрузку с другими методами оптимизации:

  1. Разделение по страницам: Загружать контроллеры только для страниц, где они используются.
  2. Деление на функции: Выносить тяжелую логику в отдельные модули и загружать их асинхронно.
  3. Предзагрузка: В случаях, когда известно, что пользователь скоро взаимодействует с элементом, можно использовать preload для уменьшения задержки.

Ленивая загрузка контроллеров является мощным инструментом оптимизации, позволяющим строить масштабируемые Stimulus-приложения с минимальной нагрузкой на начальную загрузку и плавной реакцией интерфейса.