Mithril.js — минималистичный фреймворк для построения одностраничных приложений (SPA), который сочетает простоту с высокой производительностью. Ключевой аспект разработки на Mithril — эффективное управление состоянием приложения. Несмотря на минимализм фреймворка, существуют проверенные подходы и паттерны, позволяющие организовать состояние структурировано и предсказуемо.
Каждый компонент в Mithril может содержать собственное состояние через обычные свойства объекта:
const Counter = {
count: 0,
view: function() {
return m("div", [
m("span", `Счетчик: ${this.count}`),
m("button", { onclick: () => this.count++ }, "Увеличить")
]);
}
};
Особенности локального состояния:
Недостатки:
Для управления состоянием на уровне всего приложения удобно использовать отдельные модули, которые экспортируют объект с данными и методами управления.
export const Store = {
todos: [],
add(todo) {
this.todos.push(todo);
},
remove(index) {
this.todos.splice(index, 1);
}
};
Использование в компоненте:
const TodoList = {
view: () => m("ul", Store.todos.map((todo, index) =>
m("li", [
todo,
m("button", { onclick: () => Store.remove(index) }, "Удалить")
])
))
};
Плюсы:
Минусы:
Mithril поддерживает реактивное обновление компонентов при изменении данных. Один из подходов — использование функций-геттеров и сеттеров.
const State = (() => {
let count = 0;
const listeners = new Set();
return {
getCount: () => count,
increment: () => {
count++;
listeners.forEach(fn => fn());
},
subscribe: (fn) => listeners.add(fn)
};
})();
const Counter = {
oninit: (vnode) => {
State.subscribe(() => m.redraw());
},
view: () => m("div", [
m("span", `Счетчик: ${State.getCount()}`),
m("button", { onclick: () => State.increment() }, "Увеличить")
])
};
Преимущества реактивного подхода:
Для крупных приложений удобен поток данных в одном направлении. Mithril легко интегрируется с Redux-подобными хранилищами:
const Store = {
state: { count: 0 },
listeners: [],
dispatch(action) {
switch(action.type) {
case 'INCREMENT':
this.state.count++;
break;
case 'DECREMENT':
this.state.count--;
break;
}
this.listeners.forEach(fn => fn());
},
subscribe(fn) {
this.listeners.push(fn);
}
};
const Counter = {
oninit: () => Store.subscribe(() => m.redraw()),
view: () => m("div", [
m("span", `Счетчик: ${Store.state.count}`),
m("button", { onclick: () => Store.dispatch({ type: 'INCREMENT' }) }, "+"),
m("button", { onclick: () => Store.dispatch({ type: 'DECREMENT' }) }, "-")
])
};
Особенности:
Для разделения логики состояния и визуального представления применяется слой сервисов или виртуальные модели. Компоненты получают данные через методы сервисов, а не напрямую:
const TodoService = {
todos: [],
fetchTodos() {
return fetch("/api/todos")
.then(res => res.json())
.then(data => this.todos = data);
},
addTodo(todo) {
this.todos.push(todo);
}
};
const TodoList = {
oninit: () => TodoService.fetchTodos().then(() => m.redraw()),
view: () => m("ul", TodoService.todos.map(todo => m("li", todo)))
};
Преимущества:
Эти паттерны позволяют выстроить структуру приложения, сохранять предсказуемость поведения компонентов и упростить сопровождение кода при росте проекта. Mithril предоставляет гибкость, благодаря чему можно выбирать подход, оптимальный под конкретные задачи, не перегружая приложение лишними библиотеками.