Mithril — лёгкий и современный JavaScript-фреймворк для построения SPA, который отлично подходит для создания изоморфных приложений. Изоморфные приложения (isomorphic/universal) позволяют выполнять один и тот же код как на клиенте, так и на сервере, обеспечивая быстрый первичный рендеринг и улучшенную SEO-оптимизацию.
Mithril предоставляет возможность серверного рендеринга с помощью
функции m.render в сочетании с m-виртуальным
DOM. Основная идея заключается в том, чтобы на сервере сформировать
HTML-строку, которая будет отправлена клиенту, а затем клиентский код
“подхватывает” эту разметку и превращает её в интерактивное
приложение.
Пример серверного рендеринга:
import m from "mithril";
import fs from "fs";
const App = {
view: () => m("div", "Привет из сервера!")
};
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mithril SSR</title>
</head>
<body>
${m.renderToString(App)}
<script src="/bundle.js"></script>
</body>
</html>
`;
fs.writeFileSync("index.html", html);
В этом примере m.renderToString(App) генерирует HTML,
который полностью готов к отдаче клиенту. После загрузки страницы
клиентский JavaScript подключается к уже существующему DOM, делая
приложение интерактивным.
После серверного рендеринга необходимо “гидратировать” DOM на
клиенте, чтобы Mithril мог управлять состоянием и обновлениями. Для
этого используется m.mount:
import m from "mithril";
import App from "./App";
m.mount(document.body, App);
Важно, чтобы структура виртуального DOM, созданная на сервере, совпадала с тем, что ожидает клиент, иначе возникнут лишние перерисовки или ошибки синхронизации.
В изоморфных приложениях особенно важно корректно управлять маршрутизацией и асинхронными данными. Mithril предоставляет встроенный роутер:
import m from "mithril";
const Home = { view: () => m("h1", "Главная") };
const About = { view: () => m("h1", "О проекте") };
m.route(document.body, "/", {
"/": Home,
"/about": About
});
Для серверного рендеринга маршруты должны быть известны заранее, чтобы сервер мог определить, какой компонент рендерить по URL. В реальных проектах это часто реализуется через универсальный роутер, который на сервере возвращает HTML для нужного пути, а на клиенте продолжает работу с интерактивностью.
Изоморфные приложения требуют согласованного управления данными на
сервере и клиенте. Обычно это реализуется через функции
oninit или асинхронные методы, возвращающие промисы:
const User = {
data: null,
oninit: async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
User.data = await response.json();
},
view: () => User.data ? m("div", User.data.name) : m("div", "Загрузка...")
};
На сервере необходимо дождаться завершения асинхронных операций перед
вызовом m.renderToString, чтобы HTML содержал готовые
данные.
Для создания полноценного изоморфного приложения часто используют Express:
import express from "express";
import m from "mithril";
import App from "./App";
const server = express();
server.get("*", (req, res) => {
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mithril SSR</title>
</head>
<body>
${m.renderToString(App)}
<script src="/bundle.js"></script>
</body>
</html>
`;
res.send(html);
});
server.listen(3000);
В этом примере все запросы обрабатываются сервером, который возвращает уже готовый HTML. Клиент подключает JavaScript-бандл и делает приложение интерактивным.
Mithril не навязывает сложные решения для состояния. Для изоморфных приложений рекомендуется использовать простой паттерн “store” или интегрировать сторонние библиотеки. Главное — синхронизировать состояние между сервером и клиентом. Например:
export const store = {
user: null,
async fetchUser() {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
this.user = await response.json();
}
};
Сервер может вызвать store.fetchUser() перед
рендерингом, чтобы HTML содержал актуальные данные.
Изоморфные приложения на Mithril сочетают преимущества серверного
рендеринга и клиентской интерактивности. Использование
m.renderToString, правильная гидратация и управление
асинхронными данными позволяют создавать быстрые, SEO-дружелюбные и
лёгкие приложения с минимальными накладными расходами.