Адаптивный дизайн в приложениях на Mithril строится на сочетании возможностей CSS и реактивной модели представлений, которую предоставляет сам фреймворк. Mithril не навязывает собственную систему работы с адаптивностью, что позволяет использовать проверенные подходы веб-разработки и гибко интегрировать их в компонентную архитектуру.
Mithril ориентирован на создание одностраничных приложений с минимальным весом и высокой производительностью. Адаптивный дизайн в этом контексте решает несколько задач:
Адаптивность в Mithril обычно разделяется на два уровня:
Основной механизм адаптивного дизайна — медиа-запросы CSS. Mithril никак не ограничивает их использование, так как он отвечает только за генерацию DOM-дерева.
Пример базового медиа-запроса:
.container {
display: flex;
flex-direction: row;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
}
Компонент Mithril при этом остаётся неизменным:
const Layout = {
view() {
return m(".container", [
m(".sidebar", "Sidebar"),
m(".content", "Content")
])
}
}
Такой подход предпочтителен, когда адаптация касается только внешнего вида, а не логики приложения.
Когда различия между мобильной и десктопной версиями затрагивают структуру интерфейса, применяется условный рендеринг.
Ширина экрана может храниться в состоянии:
const Screen = {
width: window.innerWidth
}
window.addEventListener("resize", () => {
Screen.width = window.innerWidth
m.redraw()
})
Использование в компоненте:
const Header = {
view() {
return Screen.width < 768
? m(".header-mobile", "Mobile Header")
: m(".header-desktop", "Desktop Header")
}
}
Ключевой момент — явный вызов
m.redraw() при изменении глобального состояния вне
жизненного цикла Mithril.
Для уменьшения связности рекомендуется выносить логику определения типа экрана в отдельный модуль.
const Media = {
isMobile() {
return window.matchMedia("(max-width: 768px)").matches
}
}
Использование:
const Menu = {
view() {
if (Media.isMobile()) {
return m(".menu-mobile")
}
return m(".menu-desktop")
}
}
Такой подход упрощает тестирование и централизует работу с медиаправилами.
window.matchMedia позволяет реагировать на изменение
условий без постоянного прослушивания resize-событий.
const mediaQuery = window.matchMedia("(max-width: 768px)")
const MediaState = {
mobile: mediaQuery.matches
}
mediaQuery.addEventListener("change", e => {
MediaState.mobile = e.matches
m.redraw()
})
Компонент:
const Layout = {
view() {
return MediaState.mobile
? m(MobileLayout)
: m(DesktopLayout)
}
}
Этот способ более точен и экономичен, чем отслеживание изменения размеров окна.
В Mithril удобно создавать отдельные компоненты под разные сценарии отображения и комбинировать их на уровне контейнера.
const MobileCard = {
view({ attrs }) {
return m(".card.mobile", attrs.title)
}
}
const DesktopCard = {
view({ attrs }) {
return m(".card.desktop", [
m("h3", attrs.title),
m("p", attrs.description)
])
}
}
Контейнер:
const Card = {
view(vnode) {
return MediaState.mobile
? m(MobileCard, vnode.attrs)
: m(DesktopCard, vnode.attrs)
}
}
Такой подход:
Иногда адаптивность затрагивает не только компоненты, но и маршруты.
m.route(document.body, "/", {
"/": {
render() {
return MediaState.mobile
? m(MobileHome)
: m(DesktopHome)
}
}
})
Этот приём используется для радикально разных интерфейсов при сохранении общей бизнес-логики.
При использовании CSS-in-JS (например, через inline-стили) медиа-запросы теряют часть выразительности. В таких случаях адаптивность чаще переносится на уровень логики.
const Box = {
view() {
const style = MediaState.mobile
? { padding: "8px" }
: { padding: "24px" }
return m(".box", { style }, "Content")
}
}
Этот способ допустим для простых сценариев, но не заменяет полноценные CSS-медиа-запросы.
Частые изменения состояния, связанные с адаптивностью, могут приводить к лишним перерисовкам. Mithril предоставляет возможность ограничивать redraw:
mediaQuery.addEventListener("change", e => {
MediaState.mobile = e.matches
m.redraw.strategy("diff")
m.redraw()
})
Также важно избегать вычислений размеров внутри view,
перенося их в состояние или lifecycle-методы.
Mithril отлично сочетается с:
Компонентная модель фреймворка позволяет использовать адаптивные классы без дополнительной логики:
m(".grid grid-cols-3 md:grid-cols-1", ...)
В этом случае вся адаптивность полностью выносится в CSS, а Mithril отвечает только за структуру.
Выбор между CSS-медиа-запросами и условным рендерингом определяется уровнем изменений:
Такое разделение позволяет сохранять простоту компонентов и не перегружать их логикой, не относящейся напрямую к отображению данных.