Представьте, что вы заходите на сайт, и он не перезагружается каждый раз, когда вы кликаете по ссылке. Всё работает плавно, как в настольной программе. Это и есть одностраничное приложение, или SPA. В этой статье я покажу, как написать такое приложение на чистом JavaScript без фреймворков. Вы удивитесь, но это проще, чем кажется.

Что такое SPA и зачем оно нужно?

SPA — это веб-приложение, которое загружается один раз, а потом обновляет только нужные части страницы. Пользователь не видит белых экранов и миганий. Всё происходит быстро и отзывчиво. Так работают Gmail, Trello, Notion.

Основные плюсы SPA:

  • Быстрый отклик интерфейса
  • Меньше трафика (не нужно каждый раз качать весь HTML)
  • Похоже на мобильное приложение
  • Легко кэшировать данные

Но есть и минусы: сложнее с SEO, нужно больше JavaScript, могут быть проблемы с историей браузера. Но этим управляют современные инструменты.

Как работает SPA изнутри

В классическом сайте каждый переход — это запрос к серверу. Сервер отдаёт новую HTML-страницу. В SPA сервер отдаёт только один HTML-файл, а дальше JavaScript управляет всем.

Основные компоненты SPA:

  • Роутер — определяет, какой компонент показать в зависимости от URL
  • Шаблонизатор — создаёт HTML на основе данных
  • Состояние — хранит данные приложения (например, список задач)
  • События — обрабатывают клики, ввод текста и т.д.

Когда пользователь кликает по ссылке, JavaScript меняет URL в адресной строке (без перезагрузки), подгружает данные и обновляет часть страницы. Всё это происходит в браузере.

Пишем простой роутер на JavaScript

Начнём с самого главного — роутера. Он будет смотреть на адресную строку и решать, что показывать. Вот пример:

class Router {
  constructor() {
    this.routes = {};
    window.addEventListener('hashchange', () => this.resolve());
  }

  addRoute(path, handler) {
    this.routes[path] = handler;
  }

  resolve() {
    const hash = window.location.hash.slice(1) || '/';
    const handler = this.routes[hash];
    if (handler) {
      handler();
    } else {
      this.routes['404']();
    }
  }
}

// Использование
const router = new Router();
router.addRoute('/', () => {
  document.getElementById('app').innerHTML = '

Главная

'; }); router.addRoute('/about', () => { document.getElementById('app').innerHTML = '

О нас

'; }); router.addRoute('404', () => { document.getElementById('app').innerHTML = '

404 — Страница не найдена

'; }); router.resolve();

Здесь мы используем хеши (#/about). Когда хеш меняется, срабатывает событие hashchange. Роутер находит нужный обработчик и обновляет содержимое контейнера с id app.

Правило: Всегда обрабатывайте случай, когда маршрут не найден. Добавьте страницу 404. Иначе пользователь увидит пустой экран и уйдёт.

Управление состоянием приложения

Состояние — это данные, которые меняются со временем. Например, список товаров в корзине или текущий пользователь. В SPA состояние хранят в объекте и обновляют через функции.

const state = {
  items: [],
  currentUser: null,
  loading: false
};

function addItem(item) {
  state.items.push(item);
  render(); // перерисовываем интерфейс
}

function removeItem(id) {
  state.items = state.items.filter(item => item.id !== id);
  render();
}

function render() {
  const list = document.getElementById('list');
  list.innerHTML = state.items.map(item => `
  • ${item.name}
  • `).join(''); }

    Главное правило: не меняйте DOM напрямую. Сначала обновите состояние, потом вызовите render. Это сделает код предсказуемым.

    Работа с сервером: загрузка данных

    SPA часто общается с сервером через API. Используем fetch для получения данных. Вот пример загрузки списка пользователей:

    async function loadUsers() {
      state.loading = true;
      render();
    
      try {
        const response = await fetch('/api/users');
        const users = await response.json();
        state.users = users;
        state.loading = false;
        render();
      } catch (error) {
        state.error = 'Ошибка загрузки';
        state.loading = false;
        render();
      }
    }

    Обратите внимание: мы показываем индикатор загрузки (state.loading), потом скрываем его. Это улучшает пользовательский опыт. Если произошла ошибка — показываем сообщение.

    Сборка всего вместе: пример SPA

    Теперь давайте соберём мини-приложение: список задач с возможностью добавлять и удалять. Вот полный код:

    В этом примере всё в одном файле. В реальном проекте вы бы разбили код на модули, но для обучения сойдёт.

    Компонент Описание Пример
    Роутер Управляет навигацией hashchange
    Состояние Хранит данные Объект state
    Рендер Обновляет DOM innerHTML
    Обработчики Реагируют на действия addEventListener
    Лайфхак: Используйте data-атрибуты для идентификации элементов. Это избавит от необходимости искать элементы по классам или id внутри обработчиков.

    Итог

    Мы разобрали, как создать простое SPA на чистом JavaScript. Вы узнали про роутер, состояние, рендер и работу с сервером. Теперь вы можете написать своё приложение без фреймворков. Начните с малого: список задач или простой блог. Со временем вы сможете добавить модульность, шаблоны и даже свой мини-фреймворк.

    Помните: SPA — это не магия, а просто JavaScript, который обновляет страницу по частям. Не бойтесь экспериментировать и пишите код с удовольствием.

    Студия WNDER