[К содержанию](../readme.md#введение-в-web-разработку)
# Single-page application
Обычный сайт состоит из множества 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 и другие. В любом случая для проекта нужны более компетентные разработчики.
## Создание SPA приложения.
**Vue** хорошо подходит для создания **SPA**. Для большинства **SPA** рекомендуется использовать официально поддерживаемую [библиотеку Vue Router](https://github.com/vuejs/router). Более подробную информацию по Vue Router можно найти в [документации](https://vue-router-ru.netlify.app/).
Создадим новое приложение **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`
```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`) тоже произошли изменения:
```vue
```
Нам здесь интересны два компонента:
* **RouterLink** добавляет ссылку на другой роут (путь). Аттрибут `to` содержит путь (которому должен соответствовать маршрут в файле `routes/index.js`)
* **RouterView** "рисует" компонент, соответствующий текушему роуту (для `/` **HomeView**, для `/about` **AboutView**).
В "рыбе" компоненты рисуются рядом с заголовком (в принципе это достаточно распространённый вариант, когда в приложении есть общее для всех страниц главное меню и/или подвал), но никто не запрещает выводить только **RouterView**, например
```vue
```
Такой вариант подходит, если общих элементов на страницах нет.
## Программное управление маршрутами
Для управления маршрутизацией есть несколько методов:
* _back_ - возвращает на предыдущую страницу
* _forward_ - переход на следующую страницу (естественно если в истории браузера есть следующая страница)
* _push_ - переходит на новую страницу, добавляя её в историю браузера
* _replace_ - открывает новую страницу не меняя историю браузера (т.е. нельзя будет вернуться на предыдущую страницу)
* _go(int)_ - переход вперед или назад на указанное количество страниц (в параметрах передаётся целое число)
При создании роутера в глобальные параметры добавляется переменная `$router`, а также метод _useRouter()_
Переменную `$router` можно использовать в шаблонах, например на странице **AboutView** добавить возврат по клику на кнопку:
```html
```
Если перед переходом нужно выполнить какие-то действия, то можно вызвать из кода:
```html
```
```js
const router = useRouter()
async function someFun() {
// делаем что нужно
await someApiFun()
// переходим куда нужно
router.back()
}
```
---
**Задание**
1. Удалить лишнее из "рыбы"
1. Добавить новые маршрут и страницу
1. Нарисовать разные варианты переходов между страницами (в шаблоне, в коде разными методами)
[Назад](./web_15.md) | [Дальше](./web_17.md)