Fetch API в компонентах

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

Основы работы Fetch API

fetch — это встроенный в браузер метод для выполнения HTTP-запросов. Его базовый синтаксис:

fetch(url, options)
  .then(response => {
    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
    return response.json();
  })
  .then(data => {
    // обработка полученных данных
  })
  .catch(error => {
    console.error('Ошибка запроса:', error);
  });
  • url — адрес ресурса.
  • options — объект с настройками запроса: метод, заголовки, тело и др.
  • response.json() — преобразует ответ в объект JSON.
  • Ошибки обрабатываются через блок catch.

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

Инициализация данных через fetch

Slim.js позволяет задать методы жизненного цикла компонента, такие как onBeforeRender или onConnected, которые идеально подходят для асинхронной загрузки данных. Пример компонента с загрузкой данных при подключении к DOM:

class UserList extends Slim {
  constructor() {
    super();
    this.users = [];
  }

  onConnected() {
    this.loadUsers();
  }

  async loadUsers() {
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/users');
      if (!response.ok) throw new Error(`Ошибка загрузки: ${response.status}`);
      this.users = await response.json();
      this.update(); // обновление DOM
    } catch (error) {
      console.error('Не удалось загрузить пользователей:', error);
    }
  }

  get template() {
    return `
      <ul>
        ${this.users.map(user => `<li>${user.name}</li>`).join('')}
      </ul>
    `;
  }
}

customElements.define('user-list', UserList);

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

  • Использование async/await упрощает работу с асинхронными запросами.
  • Метод this.update() информирует Slim о необходимости перерисовать компонент.
  • Данные хранятся как свойства класса, что позволяет автоматически реагировать на их изменения.

Обработка состояний загрузки и ошибок

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

class PostsComponent extends Slim {
  constructor() {
    super();
    this.posts = [];
    this.loading = false;
    this.error = null;
  }

  onConnected() {
    this.fetchPosts();
  }

  async fetchPosts() {
    this.loading = true;
    this.error = null;
    this.update();

    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts');
      if (!response.ok) throw new Error('Ошибка загрузки данных');
      this.posts = await response.json();
    } catch (err) {
      this.error = err.message;
    } finally {
      this.loading = false;
      this.update();
    }
  }

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

customElements.define('posts-component', PostsComponent);

Особенности реализации:

  • loading позволяет показать индикатор процесса.
  • error отображает текст ошибки.
  • finally гарантирует, что состояние загрузки будет снято вне зависимости от результата запроса.

Динамические запросы и взаимодействие с пользователем

Slim.js поддерживает реактивность и двустороннюю привязку событий. Это позволяет делать запросы по действию пользователя, например, при нажатии кнопки или изменении значения поля:

class SearchUsers extends Slim {
  constructor() {
    super();
    this.query = '';
    this.results = [];
  }

  async search() {
    if (!this.query) return;
    try {
      const response = await fetch(`https://api.example.com/users?search=${encodeURIComponent(this.query)}`);
      if (!response.ok) throw new Error('Ошибка поиска');
      this.results = await response.json();
      this.update();
    } catch (error) {
      console.error(error);
    }
  }

  onInputChange(event) {
    this.query = event.target.value;
  }

  get template() {
    return `
      <input type="text" onin put="this.getSlim().onInputChange(event)" placeholder="Поиск пользователей" />
      <button oncl ick="this.getSlim().search()">Найти</button>
      <ul>
        ${this.results.map(user => `<li>${user.name}</li>`).join('')}
      </ul>
    `;
  }
}

customElements.define('search-users', SearchUsers);

Особенности подхода:

  • onInputChange обновляет состояние query при каждом вводе.
  • Запрос выполняется только при нажатии кнопки, предотвращая лишние вызовы.
  • this.getSlim() используется для доступа к методам Slim внутри событийных атрибутов.

Совмещение Fetch с реактивными свойствами

Slim.js позволяет автоматически перерисовывать компонент при изменении свойств. Использование реактивных свойств с fetch повышает удобство:

class WeatherComponent extends Slim {
  static get observedAttributes() { return ['city']; }

  constructor() {
    super();
    this.city = '';
    this.weather = null;
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'city' && newValue !== oldValue) {
      this.loadWeather(newValue);
    }
  }

  async loadWeather(city) {
    try {
      const response = await fetch(`https://api.weatherapi.com/v1/current.json?key=API_KEY&q=${city}`);
      this.weather = await response.json();
      this.update();
    } catch (error) {
      console.error(error);
    }
  }

  get template() {
    if (!this.weather) return `<p>Введите город для получения прогноза</p>`;
    return `
      <h2>Погода в ${this.weather.location.name}</h2>
      <p>Температура: ${this.weather.current.temp_c}°C</p>
      <p>Состояние: ${this.weather.current.condition.text}</p>
    `;
  }
}

customElements.define('weather-component', WeatherComponent);

Особенности:

  • Реакция на изменение атрибута city автоматически вызывает асинхронный запрос.
  • Данные сохраняются в свойстве компонента и обновляют DOM через this.update().

Рекомендации по работе с Fetch в Slim.js

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

Slim.js и Fetch API вместе позволяют строить компактные, динамичные компоненты с асинхронными данными, сохраняя чистоту кода и высокую производительность.