SkateJS — это современная библиотека для создания веб-компонентов на JavaScript, предоставляющая простой, декларативный API для управления жизненным циклом элементов, реактивными свойствами и событиями. Одной из ключевых особенностей является возможность интеграции с внешними источниками данных, включая GraphQL, что позволяет строить динамические компоненты с минимальными усилиями.
GraphQL — это язык запросов для API, позволяющий клиенту точно указывать, какие данные нужны, и получать их в одной структуре. В сочетании со SkateJS он открывает возможности для построения компонентов с реактивными интерфейсами и оптимизированным обменом данными.
В SkateJS каждый компонент определяется с помощью функции
skate, куда передается объект конфигурации с жизненным
циклом, свойствами и рендерингом. Интеграция с GraphQL начинается с
определения свойства для хранения данных и метода для выполнения
запроса.
import { skate, h } from 'skatejs';
import { request, gql } from 'graphql-request';
const endpoint = 'https://example.com/graphql';
const USER_QUERY = gql`
query getUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
skate('user-card', {
props: {
userId: { attribute: true },
userData: { default: null }
},
async connected(element) {
if (this.userId) {
this.userData = await request(endpoint, USER_QUERY, { id: this.userId });
element.update();
}
},
render() {
if (!this.userData) {
return h('div', 'Загрузка...');
}
const { name, email } = this.userData.user;
return h('div', [
h('h2', name),
h('p', email)
]);
}
});
Ключевые моменты:
props задают реактивные свойства компонента, включая
userId для идентификатора пользователя и
userData для хранения результата GraphQL.connected вызывается при присоединении элемента к
DOM, что удобно для запуска асинхронного запроса.request из graphql-request
упрощает отправку запросов и получение данных.element.update() для
повторного рендера компонента.SkateJS автоматически следит за изменением свойств компонента. Это позволяет обновлять данные при изменении входного параметра без необходимости вручную управлять состоянием.
skate('user-card', {
props: {
userId: { attribute: true },
userData: { default: null }
},
async updated(prevProps) {
if (prevProps.userId !== this.userId) {
this.userData = await request(endpoint, USER_QUERY, { id: this.userId });
}
},
render() {
return this.userData
? h('div', [
h('h2', this.userData.user.name),
h('p', this.userData.user.email)
])
: h('div', 'Загрузка...');
}
});
Особенности:
updated(prevProps) позволяет реагировать на
изменение свойств и выполнять асинхронные действия, например повторные
запросы к GraphQL.Для построения устойчивого компонента важно обрабатывать возможные ошибки сети или некорректные данные:
skate('user-card', {
props: {
userId: { attribute: true },
userData: { default: null },
error: { default: null }
},
async updated(prevProps) {
if (prevProps.userId !== this.userId) {
this.error = null;
this.userData = null;
try {
this.userData = await request(endpoint, USER_QUERY, { id: this.userId });
} catch (err) {
this.error = err.message;
}
}
},
render() {
if (this.error) return h('div', `Ошибка: ${this.error}`);
if (!this.userData) return h('div', 'Загрузка...');
return h('div', [
h('h2', this.userData.user.name),
h('p', this.userData.user.email)
]);
}
});
Преимущества подхода:
error хранит сообщение об ошибке и позволяет
отобразить его пользователю.SkateJS позволяет легко комбинировать несколько запросов и отображать результат вместе. Для этого можно хранить отдельные свойства для каждого запроса и выполнять их параллельно:
const POSTS_QUERY = gql`
query getPosts($userId: ID!) {
posts(userId: $userId) {
id
title
}
}
`;
skate('user-dashboard', {
props: {
userId: { attribute: true },
userData: { default: null },
postsData: { default: [] }
},
async updated(prevProps) {
if (prevProps.userId !== this.userId) {
const [user, posts] = await Promise.all([
request(endpoint, USER_QUERY, { id: this.userId }),
request(endpoint, POSTS_QUERY, { userId: this.userId })
]);
this.userData = user;
this.postsData = posts.posts;
}
},
render() {
if (!this.userData) return h('div', 'Загрузка...');
return h('div', [
h('h2', this.userData.user.name),
h('ul', this.postsData.map(post => h('li', post.title)))
]);
}
});
Важные аспекты:
Promise.all позволяет запускать несколько запросов
параллельно, сокращая время ожидания.SkateJS предоставляет гибкие хуки для управления состоянием данных:
connected — первый вызов при добавлении в DOM, подходит
для первичных запросов.updated(prevProps) — реагирует на изменения свойств и
выполняет повторные запросы.disconnected — освобождает ресурсы при удалении
элемента (например, отмена подписок на GraphQL Subscriptions).render — отвечает за отображение данных, учитывая
состояние загрузки и ошибок.Эти хуки позволяют полностью контролировать жизненный цикл компонента при работе с динамическими GraphQL-данными.
Для получения обновлений в реальном времени используется GraphQL Subscriptions через WebSocket. SkateJS позволяет подписываться на события и обновлять состояние:
import { createClient } from 'graphql-ws';
const client = createClient({ url: 'wss://example.com/graphql' });
const USER_UPDATED_SUBSCRIPTION = gql`
subscription onUserUpdated($id: ID!) {
userUpdated(id: $id) {
id
name
email
}
}
`;
skate('live-user-card', {
props: {
userId: { attribute: true },
userData: { default: null }
},
connected() {
client.subscribe(
{ query: USER_UPDATED_SUBSCRIPTION, variables: { id: this.userId } },
{
next: ({ data }) => {
this.userData = data.userUpdated;
this.update();
}
}
);
},
render() {
if (!this.userData) return h('div', 'Загрузка...');
return h('div', [
h('h2', this.userData.name),
h('p', this.userData.email)
]);
}
});
userData,
вызывая повторный рендер компонента.skate.connected, updated) для выполнения
GraphQL-запросов.Такой подход позволяет строить мощные веб-компоненты с оптимизированной работой с API, высокой реактивностью и полной интеграцией с современными возможностями GraphQL.