Ленивая загрузка компонентов (lazy loading) — это техника, при которой код компонентов загружается не сразу при старте приложения, а только в момент, когда он действительно нужен. В контексте Mithril это особенно актуально из-за его ориентации на компактные SPA, высокую скорость и минимальный оверхед. Правильная реализация ленивой загрузки позволяет:
Mithril не навязывает архитектуру и не предоставляет встроенного механизма ленивой загрузки компонентов, но благодаря модульной системе JavaScript и гибкости роутера такая загрузка реализуется достаточно элегантно.
Современный JavaScript предоставляет механизм import() —
асинхронный динамический импорт. Mithril полностью совместим с этим
подходом, так как компоненты представляют собой обычные объекты или
функции.
Простейший компонент Mithril:
const UserPage = {
view() {
return m("div", "Профиль пользователя");
}
};
При ленивой загрузке этот компонент выносится в отдельный файл и импортируется только при необходимости:
import("./UserPage.js");
import() возвращает Promise, что определяет
дальнейшую архитектуру загрузки и отображения компонента.
Наиболее распространённый сценарий — загрузка компонентов по
маршрутам. Mithril использует m.route, который позволяет
определить компонент для каждого URL.
Обычная конфигурация маршрутов:
m.route(document.body, "/", {
"/": Home,
"/users": Users,
"/users/:id": UserPage
});
Для ленивой загрузки вместо прямого указания компонента используется функция-обёртка, возвращающая компонент асинхронно.
Один из базовых паттернов — создание универсального loader-компонента:
function lazy(load) {
let component = null;
let loading = false;
return {
oninit() {
if (!component && !loading) {
loading = true;
load().then(m => {
component = m.default || m;
m.redraw();
});
}
},
view(vnode) {
if (component) {
return m(component, vnode.attrs);
}
return m("div", "Загрузка...");
}
};
}
Использование в маршрутах:
m.route(document.body, "/", {
"/": Home,
"/users/:id": lazy(() => import("./UserPage.js"))
});
При переходе на маршрут компонент загружается, и после завершения
загрузки вызывается m.redraw() для обновления
интерфейса.
В учебных и промышленных проектах индикатор загрузки играет важную роль. Mithril не ограничивает форму отображения состояния загрузки. Вместо простого текста может использоваться:
Пример с отдельным компонентом-заглушкой:
const Loader = {
view() {
return m(".loader", "Загрузка модуля...");
}
};
Модификация lazy:
view(vnode) {
return component
? m(component, vnode.attrs)
: m(Loader);
}
Важный аспект — предотвращение повторной загрузки одного и того же
модуля. Благодаря замыканию в функции lazy компонент
сохраняется в памяти после первой загрузки. Это поведение эквивалентно
кэшированию.
Для более сложных сценариев возможно вынесение кэша на уровень выше:
const componentCache = {};
function lazyWithCache(key, loader) {
return {
oninit() {
if (!componentCache[key]) {
loader().then(m => {
componentCache[key] = m.default || m;
m.redraw();
});
}
},
view(vnode) {
const C = componentCache[key];
return C ? m(C, vnode.attrs) : m("div", "Загрузка...");
}
};
}
Ленивая загрузка не ограничивается маршрутизацией. Внутри одного экрана возможно откладывать загрузку тяжёлых подкомпонентов — таблиц, редакторов, графиков.
Пример:
const HeavyWidget = lazy(() => import("./HeavyWidget.js"));
const Dashboard = {
view() {
return m("div", [
m("h1", "Панель управления"),
m(HeavyWidget)
]);
}
};
Такой подход особенно полезен, если компонент отображается не всегда или зависит от действий пользователя.
Mithril позволяет использовать асинхронную логику в
oninit, но сам хук не ожидает Promise. Поэтому
важно вручную вызывать m.redraw() после завершения
загрузки.
Ключевые моменты:
oninit вызывается один раз на создание vnode;view может вызываться многократно до завершения
загрузки;m.redraw() приведёт к «застывшему»
интерфейсу.При необходимости более тонкого контроля можно использовать
oncreate или внешние state-менеджеры.
Динамический импорт может завершиться с ошибкой: сетевые проблемы, отсутствие файла, ошибки сборки. Эти случаи должны обрабатываться явно.
Расширенный вариант loader:
function lazy(load) {
let component = null;
let error = null;
return {
oninit() {
load()
.then(m => {
component = m.default || m;
m.redraw();
})
.catch(e => {
error = e;
m.redraw();
});
},
view(vnode) {
if (error) {
return m("div.error", "Ошибка загрузки");
}
if (!component) {
return m("div", "Загрузка...");
}
return m(component, vnode.attrs);
}
};
}
Ленивая загрузка напрямую зависит от поддержки сборщика:
output.format.Важно, чтобы файлы компонентов были выделены в отдельные модули и не включались в основной бандл.
Mithril поощряет минимализм и контроль со стороны разработчика. Отсутствие «магии» в ленивой загрузке — сознательное решение. Использование стандартных возможностей JavaScript делает поведение приложения прозрачным и предсказуемым, а архитектуру — легко анализируемой и поддерживаемой.