Mithril — это лёгкий и высокопроизводительный JavaScript-фреймворк для создания одностраничных приложений. Одной из ключевых задач современных SPA является взаимодействие с сервером через GraphQL. Использование GraphQL вместо REST позволяет гибко запрашивать только необходимые данные, снижая объём передаваемой информации и упрощая управление состоянием приложения.
В экосистеме JavaScript существуют готовые GraphQL клиенты, такие как
Apollo Client, urql или легковесные
решения на основе fetch. В контексте Mithril можно использовать любой из
них, но для простоты и минимального размера приложения часто применяют
встроенные HTTP-запросы через m.request или
fetch.
Пример запроса с использованием m.request:
import m from "mithril";
const query = `
query GetUsers {
users {
id
name
email
}
}
`;
const fetchGraphQL = () => {
return m.request({
method: "POST",
url: "/graphql",
body: { query },
headers: { "Content-Type": "application/json" }
});
};
const UserList = {
oninit: vnode => {
vnode.state.users = [];
fetchGraphQL().then(data => {
vnode.state.users = data.data.users;
m.redraw();
});
},
view: vnode => m("ul", vnode.state.users.map(user =>
m("li", `${user.name} (${user.email})`)
))
};
m.mount(document.body, UserList);
Ключевые моменты:
m.request автоматически возвращает промис и умеет
обрабатывать JSON.body передаётся объект с полем
query.m.redraw(), чтобы
обновить интерфейс.GraphQL позволяет передавать динамические параметры с помощью переменных. Это особенно полезно для фильтров, пагинации и динамических запросов.
Пример запроса с переменной:
const query = `
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
const fetchUser = (id) => {
return m.request({
method: "POST",
url: "/graphql",
body: { query, variables: { id } },
headers: { "Content-Type": "application/json" }
});
};
const UserDetail = {
oninit: vnode => {
vnode.state.user = null;
fetchUser(1).then(data => {
vnode.state.user = data.data.user;
m.redraw();
});
},
view: vnode => vnode.state.user
? m("div", [
m("h2", vnode.state.user.name),
m("p", vnode.state.user.email)
])
: m("p", "Загрузка...")
};
m.mount(document.body, UserDetail);
Особенности:
variables.$имя: тип!.m.redraw(), если данные
загружаются асинхронно вне компонентов.GraphQL мутации позволяют создавать, обновлять и удалять данные на сервере. Работа с ними в Mithril аналогична запросам, но с ключевым отличием — после успешной мутации часто требуется локальное обновление состояния компонента.
Пример создания нового пользователя:
const mutation = `
mutation AddUser($name: String!, $email: String!) {
addUser(name: $name, email: $email) {
id
name
email
}
}
`;
const addUser = (name, email) => {
return m.request({
method: "POST",
url: "/graphql",
body: { query: mutation, variables: { name, email } },
headers: { "Content-Type": "application/json" }
});
};
const AddUserForm = {
oninit: vnode => {
vnode.state.name = "";
vnode.state.email = "";
vnode.state.users = [];
},
view: vnode => m("div", [
m("input[type=text][placeholder=Имя]", {
value: vnode.state.name,
oninput: e => vnode.state.name = e.target.value
}),
m("input[type=email][placeholder=Email]", {
value: vnode.state.email,
oninput: e => vnode.state.email = e.target.value
}),
m("button", {
onclick: () => addUser(vnode.state.name, vnode.state.email)
.then(data => {
vnode.state.users.push(data.data.addUser);
m.redraw();
})
}, "Добавить пользователя"),
m("ul", vnode.state.users.map(user =>
m("li", `${user.name} (${user.email})`)
))
])
};
m.mount(document.body, AddUserForm);
Важные моменты:
m.redraw() критично для обновления
интерфейса.Для приложений с реальным временем можно подключать подписки через WebSocket. В Mithril интеграция чаще всего происходит через сторонние библиотеки, такие как subscriptions-transport-ws, с последующей обработкой данных в состоянии компонентов.
Пример базовой подписки:
import { SubscriptionClient } from 'subscriptions-transport-ws';
const client = new SubscriptionClient('ws://localhost:4000/graphql', { reconnect: true });
const NewUserComponent = {
oninit: vnode => {
vnode.state.users = [];
client.request({ query: `subscription { newUser { id name email } }` })
.subscribe({
next: data => {
vnode.state.users.push(data.data.newUser);
m.redraw();
}
});
},
view: vnode => m("ul", vnode.state.users.map(user =>
m("li", `${user.name} (${user.email})`)
))
};
m.mount(document.body, NewUserComponent);
Особенности подписок:
m.redraw() для
обновления интерфейса при поступлении новых данных.Для масштабных приложений рекомендуется выносить GraphQL-запросы и мутации в отдельные модули. Это улучшает читаемость и облегчает тестирование.
Пример структуры:
/api
graphql.js // функции fetchGraphQL, fetchUser, addUser
/components
UserList.js
UserDetail.js
AddUserForm.js
Принципы:
m.request для лёгких проектов без сложного
кеширования.m.redraw()
после получения асинхронных данных.