Загрузка данных при монтировании

Slim.js — это легковесная библиотека для создания веб-компонентов, ориентированная на декларативный подход и реактивность. Одной из ключевых задач при работе с компонентами является загрузка данных на этапе их монтирования. Slim.js предоставляет удобные хуки жизненного цикла, которые позволяют выполнить асинхронные операции и инициализировать состояние компонента до его отображения.


Жизненный цикл компонента и хук onBeforeMount

Каждый компонент Slim.js проходит через несколько фаз жизненного цикла. Для загрузки данных непосредственно перед отображением используется хук onBeforeMount. В этом методе выполняются операции, которые должны завершиться до того, как DOM компонента будет полностью смонтирован:

import Slim from 'slim-js';

class UserProfile extends Slim {
  static get observedAttributes() { return ['userId']; }

  onBeforeMount() {
    this.userData = null;
    this.loadUserData(this.getAttribute('userId'));
  }

  async loadUserData(userId) {
    const response = await fetch(`https://api.example.com/users/${userId}`);
    this.userData = await response.json();
    this.update(); // Перерисовывает шаблон после получения данных
  }

  get template() {
    return `
      <div>
        ${this.userData ? `<h2>${this.userData.name}</h2>` : `<p>Загрузка...</p>`}
      </div>
    `;
  }
}

Slim.define('user-profile', UserProfile);

Ключевые моменты:

  • onBeforeMount вызывается один раз перед первичной отрисовкой.
  • Асинхронные функции, вызываемые в этом хуке, позволяют загрузить данные до взаимодействия пользователя с компонентом.
  • После изменения состояния вызывается this.update(), чтобы обновить DOM.

Использование onMount для асинхронной инициализации

Хук onMount вызывается после того, как компонент уже добавлен в DOM. Это удобно, если данные можно загружать параллельно с отображением или если требуется доступ к реальному DOM-элементу компонента:

class TodoList extends Slim {
  todos = [];

  onMount() {
    this.fetchTodos();
  }

  async fetchTodos() {
    const response = await fetch('https://api.example.com/todos');
    this.todos = await response.json();
    this.update();
  }

  get template() {
    return `
      <ul>
        ${this.todos.map(todo => `<li>${todo.title}</li>`).join('')}
      </ul>
    `;
  }
}

Slim.define('todo-list', TodoList);

Особенности использования onMount:

  • Доступ к реальному DOM через this.shadowRoot или this.querySelector.
  • Подходит для работы с библиотеками, которые требуют наличия элемента в DOM (например, графические библиотеки).
  • Асинхронная загрузка не блокирует первичную отрисовку, поэтому пользователь видит “заглушку” или состояние загрузки.

Работа с состоянием компонента и реактивность

Slim.js автоматически отслеживает свойства компонента, изменяя DOM при вызове this.update(). При загрузке данных важно правильно управлять состоянием:

class WeatherWidget extends Slim {
  weather = null;
  isLoading = true;

  onMount() {
    this.loadWeather();
  }

  async loadWeather() {
    const response = await fetch('https://api.weather.com/v1/current');
    this.weather = await response.json();
    this.isLoading = false;
    this.update();
  }

  get template() {
    return this.isLoading
      ? `<p>Загрузка погоды...</p>`
      : `<div>Температура: ${this.weather.temp}°C</div>`;
  }
}

Slim.define('weather-widget', WeatherWidget);

Выводы по работе с состоянием:

  • Создание флага загрузки (isLoading) позволяет управлять визуальной частью компонента.
  • Асинхронная функция изменяет состояние, а this.update() инициирует реактивное обновление DOM.
  • Slim.js позволяет отделять логику загрузки от отрисовки, упрощая поддержку больших компонентов.

Обработка ошибок при загрузке данных

Асинхронные операции могут завершиться ошибкой. Для стабильности приложения важно обрабатывать исключения и отображать соответствующее состояние:

class PostsList extends Slim {
  posts = [];
  error = null;

  onMount() {
    this.fetchPosts();
  }

  async fetchPosts() {
    try {
      const response = await fetch('https://api.example.com/posts');
      if (!response.ok) throw new Error('Ошибка загрузки');
      this.posts = await response.json();
    } catch (err) {
      this.error = err.message;
    } finally {
      this.update();
    }
  }

  get template() {
    if (this.error) return `<p>Ошибка: ${this.error}</p>`;
    if (!this.posts.length) return `<p>Загрузка...</p>`;
    return `<ul>${this.posts.map(p => `<li>${p.title}</li>`).join('')}</ul>`;
  }
}

Slim.define('posts-list', PostsList);

Важные аспекты:

  • Использование блока try/catch/finally для безопасной загрузки данных.
  • Поддержка отображения состояния ошибки без нарушения структуры DOM.
  • Любые изменения состояния должны сопровождаться вызовом this.update() для перерисовки.

Итоговая структура асинхронного компонента

  1. Инициализация свойств — создаются поля для хранения данных и состояния загрузки.
  2. Выбор подходящего хука жизненного циклаonBeforeMount для загрузки до отображения, onMount для работы с DOM.
  3. Асинхронная загрузка данных — использование fetch или других API с async/await.
  4. Обновление состояния — изменение свойств компонента и вызов this.update().
  5. Обработка ошибок — через try/catch, чтобы компонент корректно реагировал на сбои.

Slim.js делает процесс интеграции асинхронных данных в компоненты простым и предсказуемым, позволяя создавать динамичные и отзывчивые интерфейсы без лишнего кода.