Многие думают, что для создания современного одностраничного приложения (SPA) обязательно нужны React, Vue или Angular. Но что, если я скажу, что всё это можно сделать на чистом JavaScript, без единой библиотеки? И это будет не только просто, но и полезно для понимания того, как всё работает под капотом. В этой статье мы разберём, как построить SPA на нативном JS, и вы увидите, что это не так страшно, как кажется.
Что такое SPA и зачем он нужен?
SPA (Single Page Application) — это веб-приложение, которое загружает одну HTML-страницу и динамически обновляет её содержимое при взаимодействии с пользователем. Вместо того чтобы каждый раз запрашивать новую страницу с сервера, SPA подгружает только необходимые данные и перерисовывает часть интерфейса. Это делает работу приложения быстрее и плавнее, как в нативных программах.
Представьте, что вы листаете ленту в социальной сети. Если бы каждая прокрутка вызывала полную перезагрузку страницы, вы бы сошли с ума. SPA решает эту проблему: контент подгружается незаметно, а вы продолжаете скроллить.
Основные компоненты SPA на чистом JavaScript
Чтобы построить SPA, нужно реализовать несколько ключевых механизмов:
- Роутинг — переключение между «страницами» без перезагрузки. Обычно используется хэш (`#`) или History API.
- Шаблонизация — динамическое создание HTML-элементов на основе данных.
- Управление состоянием — хранение данных приложения и их обновление.
- Обработка событий — реакция на действия пользователя.
Всё это можно сделать с помощью встроенных возможностей браузера: `window.location`, `history.pushState`, `document.createElement`, `addEventListener` и других.
Пример простого SPA на нативном JavaScript
Давайте создадим мини-приложение с двумя «страницами»: главная и о нас. Для навигации используем хэш.
Сначала HTML-каркас:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>SPA на чистом JS</title>
</head>
<body>
<nav>
<a href="#home">Главная</a>
<a href="#about">О нас</a>
</nav>
<div id="app"></div>
<script src="app.js"></script>
</body>
</html>
Теперь JavaScript (app.js):
// Функция для отображения контента в зависимости от хэша
function router() {
const app = document.getElementById('app');
const hash = window.location.hash || '#home';
switch(hash) {
case '#home':
app.innerHTML = '<h1>Главная страница</h1><p>Добро пожаловать!</p>';
break;
case '#about':
app.innerHTML = '<h1>О нас</h1><p>Мы делаем SPA на чистом JS.</p>';
break;
default:
app.innerHTML = '<h1>404</h1><p>Страница не найдена</p>';
}
}
// Слушаем изменения хэша
window.addEventListener('hashchange', router);
// Инициализация при загрузке
window.addEventListener('load', router);
Этот код отслеживает изменение хэша и перерисовывает содержимое `#app`. Всё просто, не правда ли?
Продвинутый роутинг с History API
Хэш-навигация проста, но выглядит не очень красиво (в URL появляется `#`). History API позволяет менять URL без перезагрузки и без хэша. Вот как это работает:
// Переход на новую страницу
function navigate(path) {
history.pushState({}, '', path);
render(path);
}
// Обработка кнопок назад/вперёд
window.addEventListener('popstate', () => {
render(window.location.pathname);
});
// Пример использования
document.querySelector('a[href="/about"]').addEventListener('click', (e) => {
e.preventDefault();
navigate('/about');
});
Однако при использовании History API сервер должен быть настроен так, чтобы все запросы возвращали один и тот же HTML-файл (index.html). Иначе при обновлении страницы вы получите 404.
Сравнение подходов к роутингу
Выбор между хэшем и History API зависит от требований. Вот краткая таблица:
| Критерий | Хэш (#) | History API |
|---|---|---|
| Простота настройки | Высокая (не нужен сервер) | Требует настройки сервера |
| Красота URL | С #, менее красиво | Чистые URL |
| SEO | Плохо индексируется | Лучше, но требует SSR |
| Поддержка браузерами | Везде | Современные браузеры |
Управление состоянием и шаблонизация
В SPA важно хранить данные и обновлять интерфейс при их изменении. Можно использовать простой объект и функцию рендеринга, которая перерисовывает компоненты при изменении состояния.
const state = {
items: ['Яблоко', 'Банан', 'Апельсин']
};
function renderList() {
const list = document.getElementById('list');
list.innerHTML = state.items.map(item => `<li>${item}</li>`).join('');
}
// Добавление элемента
function addItem(item) {
state.items.push(item);
renderList();
}
// Инициализация
renderList();
Для более сложных приложений можно применять паттерн «наблюдатель» или использовать Proxy для отслеживания изменений.
Заключение
Создание SPA на нативном JavaScript — это не только полезный опыт, но и отличный способ понять, как работают современные фреймворки. Вы можете начать с простого хэш-роутинга, затем перейти на History API, добавить управление состоянием и шаблонизацию. Главное — не бояться экспериментировать и писать код.
В итоге вы получите лёгкое, быстрое и понятное приложение, которое не зависит от сторонних библиотек. А знания, полученные в процессе, пригодятся вам при работе с любыми инструментами.



