Обычный сайт состоит из множества HTML-страниц. Вы кликаете по ссылкам, браузер загружает новые страницы по этим ссылкам, у вас появляется ощущение движения от одной страницы к другой. Страницы могут лежать как файлы на каком-то сервере или генерироваться под ваш запрос какой-то серверной программой. Но, условно говоря, каждый «экран» сайта — это отдельная техническая сущность, отдельный документ. И мы между ними перемещаемся.
В начале 2010-х появилась новая концепция: SPA — Single Page Application.
SPA работает так: когда пользователь открывает страницу, браузер загружает сразу весь код приложения. Но показывает только конкретный модуль — часть сайта, которая нужна пользователю. Когда пользователь переходит в другую часть приложения, браузер берёт уже загруженные данные и показывает ему. И, если нужно, динамически подгружает с сервера нужный контент без обновления страницы.
С одной стороны, такие приложения работают быстро и меньше нагружают сервер. С другой стороны, они требуют большей загрузки на старте.
JavaScript на стороне клиента перехватывает навигацию, динамически получает новые данные и обновляет текущую страницу без полной перезагрузки страницы. Это, как правило, обеспечивает более быстрое взаимодействие с пользователем, особенно в тех случаях, когда речь идёт о реальных "приложениях", в которых пользователь должен выполнять множество взаимодействий в течение длительного времени.
В таких SPA "маршрутизация" осуществляется на стороне клиента, в браузере.
Преимущества SPA
SPA быстрые. Переход между модулями в приложении происходит быстрее: нужные ресурсы уже загружены, нужно просто подставить данные, которые запросил пользователь. Часто при этом сервер возвращает не тяжеловесный HTML, а лёгкий JSON или XML.
Ещё использование JSON упрощает разработку приложения для разных платформ. Если для веб-версии разработать обычный сайт, который принимает от сервера HTML, то для мобильного приложения придётся писать доработку, так как там HTML не подойдёт. JSON делает ответ сервера универсальным.
SPA гибкие. Раз пользователь всё время работает с одной страницей, проще делать интересные переходы и анимацию элементов. Можно работать с состоянием кнопок, вкладок и переключателей. Таким образом, интерфейс SPA может быть похож скорее на полноценное приложение, а не на простой сайт.
SPA работают везде. Всё, что нужно для SPA — поддержка JavaScript. Такие сайты хорошо работают и на десктопе, и в вебе, могут отчасти заменить полноценные мобильные приложения.
Недостатки SPA
Проблемы с SEO. По умолчанию у приложений напряжённые отношения с поисковыми машинами: те натренированы индексировать отдельные страницы, у каждой из которых есть заголовок, описание и остальные метатеги. В SPA приходится выкручиваться.
Похожая проблема с некоторыми системами аналитики. Вот что про SPA говорит Google-аналитика: «Стандартный тег Google Аналитики хорошо подходит для обычных сайтов, поскольку его код выполняется при каждой загрузке новой страницы. Однако при работе с одностраничным приложением такой код будет выполнен лишь один раз». То есть чтобы корректно собирать аналитику, придётся самостоятельно настроить отслеживание нужных событий.
Зависимость от интернета. Для запуска веб-приложения нужна связь с сервером, так что в большинстве случаев без интернета не обойтись, как и с обычными сайтами. Этим SPA проигрывают обычным приложениям.
Хотя здесь есть исключение — если во время первой загрузки браузер получает все данные и больше ничего подгружать не нужно, то можно работать и без интернета.
SPA не для новичков. Написать такое приложение на простом HTML и CSS не получится, нужно знать JS. Часто для этого используют фреймворки — React, Angular, Vue, Ember и другие. В любом случая для проекта нужны более компетентные разработчики.
Vue хорошо подходит для создания SPA. Для большинства SPA рекомендуется использовать официально поддерживаемую библиотеку Vue Router. Более подробную информацию по Vue Router можно найти в документации.
Создадим новое приложение Vue командой
npm create vue@latest
При установке разрешите следующие пункты:
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
Pinia - это библиотека управления состояниями. С ней мы познакомимся позже
В каталоге src добивились два каталога: router и view (на stores пока не обращаем внимания, там Pinia)
Все доступные маршруты (роуты, пути) прописаны в файле src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
],
})
export default router
В массиве routes описаны маршруты, в которых указывается путь, название маршрута и компонент с содержимым для страницы
В "рыбе" приведены два разных варианта описания компонента для маршрута, но делают они одно и то же.
В основном файле приложения (App.vue) тоже произошли изменения:
<template>
<header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<RouterView />
</template>
Нам здесь интересны два компонента:
RouterLink добавляет ссылку на другой роут (путь). Аттрибут to содержит путь (которому должен соответствовать маршрут в файле routes/index.js)
RouterView "рисует" компонент, соответствующий текушему роуту (для / HomeView, для /about AboutView).
В "рыбе" компоненты рисуются рядом с заголовком (в принципе это достаточно распространённый вариант, когда в приложении есть общее для всех страниц главное меню и/или подвал), но никто не запрещает выводить только RouterView, например
<template>
<RouterView />
</template>
Такой вариант подходит, если общих элементов на страницах нет.
Для управления маршрутизацией есть несколько методов:
При создании роутера в глобальные параметры добавляется переменная $router, а также метод useRouter()
Переменную $router можно использовать в шаблонах, например на странице AboutView добавить возврат по клику на кнопку:
<button
@click="$router.back()"
>
назад
</button>
Если перед переходом нужно выполнить какие-то действия, то можно вызвать из кода:
<button
@click="someFun"
>
при клике будет вызвана функция someFun
</button>
const router = useRouter()
async function someFun() {
// делаем что нужно
await someApiFun()
// переходим куда нужно
router.back()
}
Задание