Одностраничное приложение (SPA) - парадигма современной сети. SPA в основном представляет собой одну страницу и имеет преимущество, заключающееся в простоте развертывания и предоставлении пользователю опыта, аналогичного собственным приложениям, по сравнению с традиционным рендерингом на стороне сервера.
Традиционный способ использования тега ссылки заключается в обновлении всей страницы, поскольку статические ресурсы загружаются при каждом запросе новой страницы, которая обновляется и не используется. Это неэффективно, потому что обновляет всю страницу, включая часть, которую не нужно менять.
SPA в основном загружает все статические ресурсы, необходимые для веб-приложения, один раз. Когда запрашивается новая страница, принимаются только данные, необходимые для обновления страницы, и страница обновляется. В результате общий трафик может быть уменьшен. Поскольку обновление не выполняется, Опыт может быть предоставлен.
Поскольку мобильное использование растет, сокращение трафика, скорость, удобство использования и скорость реагирования являются важными вопросами. Основная ценность SPA соответствует стратегии Mobile First, поскольку она может улучшить взаимодействие с пользователем (UX) и дополнительно повысить скорость приложения.
Существует компромисс во всех архитектурах программного обеспечения, и у SPA есть структурные недостатки, поскольку не существует серебряной пули для всех приложений. Типичные недостатки заключаются в следующем.
Начальная скорость диска Начальная скорость диска относительно низкая, поскольку SPA один раз загружает все статические ресурсы, необходимые для веб-приложения. Однако, поскольку SPA больше подходит для приложений, чем для веб-страниц, он не может быть решающим фактором, учитывая такие преимущества, как снижение трафика, скорости, удобства использования и скорости отклика. SEO (поисковая оптимизация) Проблема SPA - это асинхронная модель на основе JavaScript (метод рендеринга клиента), а не метод рендеринга сервера. Поэтому SEO всегда был недостатком. Тем не менее, SEO не является серьезной проблемой, потому что SPA - это технология, подходящая для приложений, а не веб-страница для предоставления информации. В среде SPA, такой как Angular или React, технология поддержки SEO, которая поддерживает рендеринг сервера, уже существует, и выборочная поддержка SEO возможна для страниц, которые требуют ответа SEO.
Маршрутизация - это функция, которая определяет маршрут от пункта отправления до пункта назначения. Под маршрутизацией приложений понимается возможность для пользователя управлять навигацией для переключения экранов с одного вида на другой для выполнения задачи. Обычно это последовательность действий для запроса данных на сервер и переключения экрана для интерпретации URL-адреса или события, запрошенного пользователем, и для получения данных для переключения на новую страницу.
Когда браузер переключает экран, это происходит следующим образом.
Введите URL-адрес в адресной строке браузера, чтобы перейти на страницу.
Нажав на ссылку на веб-странице, вы попадете на эту страницу.
Нажатие кнопки «Назад» или «Вперед» в браузере приведет к перемещению назад или вперед истории веб-страницы, которую посетил пользователь.
Когда AJAX-запрос генерирует ответ, получая данные с сервера, URL адресной строки браузера не изменяется. Это означает, что пользователи не могут управлять своей историей посещений, и это также является причиной проблем SEO (поисковой оптимизации). Для управления историей каждая страница должна иметь уникальный URL-адрес, который можно отличить от адресной строки браузера.
Давайте посмотрим на процесс развития от традиционного веб-метода до SPA и маршрутизации SPA.
Чтобы запустить пример, процесс выглядит следующим образом.
Чтобы запустить виртуальный сервер служить Чтобы установить.
$ npm install -g serve
Клонировать исходный код.
$ git clone https://github.com/ungmo2/spa-example.git $ cd spa-example
Вот как запустить пример.
# Традиционный метод ссылки $ npm run link # Метод AJAX $ npm run ajax # Метод хеширования $ npm run hash # PJAX метод $ npm run pjax
Традиционный метод ссылки - это основной метод работы веб-страницы, который работает с тегом ссылки. Давайте посмотрим на код ниже.
<Meta charset = "UTF-8"> <meta name = "viewport" content = "width = device-width, initial-scale = 1.0"> <meta http-эквивалент = </ Head> <body> <nav> </ link> </ link> </ li> <ul> <li> <a href="/"> Главная страница </a> </ li> <li> <a href="service.html"> Сервис </a> </ li> <li> <a> href = "about.html"> О </a> </ h1> </ h1> <p> Это главная страница </ p> </ section > </ body> </ html>
Когда вы щелкаете тег ссылки (например, <a href="service.html"> Service </a>), путь к ресурсу, который является значением атрибута href, добавляется к пути URL-адреса и отображается в адресной строке, а ресурс запрашивается с сервера.
Структура URI
В этот момент сервер отвечает клиенту полным ресурсом, достаточным для отображения экрана в формате html. Это называется рендерингом сервера . Браузер получает и отображает HTML, на который отвечает сервер. В этом случае обновление происходит потому, что вся страница перерисовывается в процессе переключения на html, полученный с предыдущей страницы.
Традиционный жизненный цикл веб-страницы
Этот метод может отображать только тот HTML-код, на который был получен ответ, без необходимости использования JavaScript, и, поскольку каждая страница имеет свой собственный URL, проблем с управлением историей и SEO-перепиской не возникает. Однако существует недостаток, заключающийся в том, что для каждого запроса необходимо получать дублирующийся ресурс, и в процессе повторной визуализации всей страницы происходит обновление.
Чтобы запустить приведенный выше пример, выполните следующую команду.
$ npm run link
Традиционный метод связывания обновляет всю страницу в процессе переключения экрана на HTML, полученный с текущей страницы, поэтому происходит обновление. Простая веб-страница не будет проблемой, но для сложных веб-страниц она будет тормозиться, потому что вам придется загружать дубликаты HTML, CSS и JavaScript при каждом запросе.
AJAX (асинхронный JavaScript и XML) появился, чтобы преодолеть недостатки традиционной компоновки. AJAX относится к способу асинхронного обмена данными между сервером и браузером с использованием JavaScript.
Жизненный цикл AJAX
Когда веб-страница возвращается с сервера, AJAX должен отображать весь экран и отображать одну и ту же страницу, даже если обновляется только часть страницы.
<Meta charset = "UTF-8"> <meta name = "viewport" content = "width = device-width, initial-scale = 1.0"> <meta http-эквивалент = <Script src = "js / index.js </ title> <link rel =" stylesheet "href =" css / style.css "> <script src =" js / index.js " <de> </ script> </ head> <body> <nav> <ul id = "navigation"> <li> <a id="home"> Главная страница </a> </ li> id = "service"> Сервис </a> </ li> <li> <a id="about"> О программе </a> </ li> root "> </ div> </ body> </ html>
Если вы посмотрите на приведенный выше пример, не используйте атрибут href в теге ссылки (<a id="home"> Главная страница </a>). Вы можете видеть, что содержимое веб-страницы частично пусто. Если щелкнуть навигацию, это предотвратит поведение тега ссылки по умолчанию и будет использовать AJAX для запроса ресурсов, необходимых для сервера. Когда запрошенный ресурс отвечает, клиент завершает HTML, заменяя его веб-страницей.
Это позволяет избежать ненужных запросов на дублирование ресурсов. Кроме того, нет необходимости повторно отображать всю страницу, и только часть, которая должна быть обновлена, может быть загружена и обновлена, поэтому можно ожидать быстрого и плавного отображения.
Реализация JavaScript заключается в следующем.
(data) {const json = JSON.parse (data); root () {function () {const root = document.querySelector ('. app-root'); const navigation = document.getElementById Функция get (url) {inner.HTML = innerHTML = `<h1> $ {json.title} </ req.onreadystatechange = function () {if (req.) {вернуть новое Promise ((разрешить, отклонить) => {const req = new XMLHttpRequest (); req.open ('GET', url) });} const route = {'home': function) {if (req.status === 200) разрешить (req.response), иначе отклонить (req.statusText); ()) {get ('/ data / service.json') .then (res => data () ()); в противном случае (page) {root ());}, 'about': function () {get ('/ data / about.html' } (route [page] || rout.otherwise (page);} // AJAX-запрос Он не меняет URL адресной строки, поэтому он не управляет историей. navigation.addEventListener ('click', e => {if (! e.target || e.target.nodeName! == 'A') return; e.preventDefault (); router (e.target.id) ); // DOMContentLoaded - это событие, которое происходит во время загрузки HTML и скрипта перед событием загрузки. (Поддержка IE 9+) // При нажатии на обновление, когда веб-страница загружается впервые, она запрашивает текущую страницу (например, loclahost: 5002) на сервере. В настоящее время AJAX запрашивает ресурсы, необходимые для Home. window.addEventListener ('DOMContentLoaded', () => router ('home')); } ());
AJAX не меняет URL-адрес, поэтому адрес в адресной строке не изменяется. Это означает, что управление историей, такое как браузер назад и вперед , не работает. Конечно, код history.back (), history.go (n) и т. Д. Работать не будет. При нажатии кнопки «Обновить» всегда происходит перезагрузка первой страницы, поскольку адрес в адресной строке не изменится. Подход AJAX, который работает с одним адресом, не свободен от проблем SEO .
Чтобы запустить приведенный выше пример, выполните следующую команду.
$ npm run ajax
Преимущество подхода AJAX состоит в том, что он позволяет избегать ненужных запросов на дублирование ресурсов и реализовывать пользовательский интерфейс без обновления, но он имеет недостаток, заключающийся в невозможности управлять историей. Хеш-метод - это метод, дополняющий это.
В хэш-методе используется привязка, которая является уникальной функцией идентификатора фрагмента URI (#service). Идентификатор фрагмента иногда называют хеш-меткой или хеш-кодом.
<Meta charset = "UTF-8"> <meta name = "viewport" content = "width = device-width, initial-scale = 1.0"> <meta http-эквивалент = <Script src = "js / index.js" </ li> </ li> </ li> <li> <a href="/"> Главная страница </a> </ li> <li> <a script> </ head> <body> <nav> "> Сервис </a> </ li> <li> <a href="#about"> О программе </a> </ li> </ ul> Загрузка ... </ div> </ body> </ html>
Если вы посмотрите на приведенный выше пример, вы используете хеш в атрибуте href тега ссылки (<a href="#service"> Сервис </a>). То есть, когда нажимается навигация, в адресной строке отображается URI с добавленным хешем. Однако, если хеш-код изменяется с тем же URL-адресом, браузер не выполняет никаких запросов к серверу. То есть, если хэш изменен, он не отправляет новый запрос на сервер, и страница не обновляется. Это связано с тем, что хеш - это не запрос, а якорь, являющийся уникальной функцией идентификатора фрагмента (#service), который предназначен для перемещения по веб-странице.
Кроме того, метод хеширования не отправляет новый запрос на сервер, поэтому страница не обновляется, но в управлении историей нет проблем, поскольку для каждой страницы существует уникальный логический URL-адрес.
Реализация JavaScript заключается в следующем.
root.innerHTML = `<h1> $ {json.title} (функция) {const root = document.querySelector ('. app-root'); функция render (данные) {const json = JSON.parse function get (url) {вернуть новое обещание ((разрешить, отклонить) =>} {/ h1> <p> $ {json.content} {if (req.readyState === XMLHttpRequest.DONE) {if (req ()) {req.open (); }); const route = {'': function () {get ('/ data / home.json'); else (reject.statusText) ), затем 'about (res => render (res));},' service ': function () {get (' / data / service.json ' ()), иначе () {root.innerHTML = `$ {location.hash} не найдено`; }}; функция router () {// получить хэш URL-адреса const hash = location.hash.replace ('#', ' в Нажав на изменения URI хэш. Поскольку uri адресной строки изменяется, управление историей возможно. // В настоящее время, если хэш-код uri изменяется, сервер не выполняет запрос. // Поэтому мы используем событие hashchange, которое происходит при изменении хеша uri, чтобы обнаружить изменение хеша и выполнить требуемый AJAX-запрос. // Недостаток метода хеширования в том, что uri содержит ненужный #. window.addEventListener ('hashchange', маршрутизатор); // DOMContentLoaded - это событие, которое происходит во время загрузки HTML и скрипта перед событием загрузки. (IE 9 и более поздние версии) // При нажатии на обновление, когда веб-страница загружается в первый раз, она запрашивает текущую страницу (например, loclahost: 5003 / # service), поэтому index.html перезагружается и событие DOMContentLoaded Роутер называется. window.addEventListener ('DOMContentLoaded', маршрутизатор); } ());
Метод хэширования - это событие, которое происходит при изменении хэша URI. hashchange События используются для обнаружения изменений хеша и выполнения требуемых запросов AJAX.
Недостатком хеш-подхода является то, что URI содержит ненужный #. В общем, мы используем #! При использовании хэша Взрыв хэш (Хэш-бах) Это называется.
Хеш-метод является переходной техникой. История HTML5 API PushState Поддерживается во всех браузерах, вам не нужно использовать хэш-удары, но в настоящее время pushState поддерживается только в некоторых браузерах (IE 10 или выше).
Другая проблема - это проблемы с SEO . Сканер следует HTTP 1.1 и спецификации URL (например, RFC-2396), чтобы поисковая система собирала содержимое веб-сайта. Поскольку эти сканеры не запускают JavaScript, они не могут собирать содержимое сайта, созданного с помощью хэша. Известно, что Google решил эту проблему, изменив хэш-ссылку на общий URL-адрес, но другие поисковые системы не могут собирать контент с сайтов, созданных с помощью хеш-метода.
Чтобы запустить приведенный выше пример, выполните следующую команду.
$ npm run hash
Самый большой недостаток хеш-подхода - это проблемы с SEO. Дополнительным методом является HTML5 Histroy API PushState с публичное мероприятие Используется. pushState и popstate работают в IE 10 и выше.
<Meta charset = "UTF-8"> <meta name = "viewport" content = "width = device-width, initial-scale = 1.0"> <meta http-эквивалент = <Script src = "js / index.js" </ li> <p> <p> <p> <p> <a href="/"> Главная страница </a> </ li> <li> <a> <li> <de> </ script> <a href="/about"> О </a> </ li> </ ul> </ nav> </ div> <div class = app-root "> Загрузка ... </ div> </ body> </ html>
Если вы посмотрите на пример выше, вы используете путь в атрибуте href тега ссылки (<a href="/service"> Сервис </a>). Когда нажата навигация, на сервер запрашивается URI с добавленным путем. Метод PJAX отлавливает события щелчков навигации и использует предотвращение ошибок для предотвращения запросов к серверу. Затем мы используем путь в атрибуте href, чтобы сделать запрос AJAX.
В настоящее время запрос AJAX не изменяет URL-адрес окна адреса, поэтому управление историей невозможно. Это метод pushState. Метод pushState изменяет URL-адрес в адресной строке и добавляет URL-адрес в качестве записи истории, но не запрашивает его.
Реализация JavaScript заключается в следующем.
(data) {const json = JSON.parse (data); root () {function () {const root = document.querySelector ('. app-root'); const navigation = document.getElementById Функция get (url) {inner.HTML = innerHTML = `<h1> $ {json.title} </ req.onreadystatechange = function () {if (req.) {вернуть новое Promise ((разрешить, отклонить) => {const req = new XMLHttpRequest (); req.open ('GET', url) });} const маршруты = {'/': функция () {если (req.status === 200) разрешить (req.response), иначе отклонить (req.statusText) () {get ('data / service.json') .then (res => render (res)); (res => render (res));}, иначе (путь)), '/ about': function () {get ('/ data / about.html' {route.innerHTML = `$ {путь} не найден`;}}; функция маршрутизатора (путь) {// входы маршрутов} События // PJAX образом, что происходит, когда изменение не использует хэш не может быть использовано событие hashchange. // Событие popstate не вызывается pushState. // Это вызвано кнопкой предыдущей / следующей страницы или history.back () / history.go (n). // e.state является первым аргументом метода pushState console.log ('[popstate]', e.state); // Если нажата кнопка предыдущей / следующей страницы, Вызовите маршрутизатор (e.state.path);}); // Нажмите кнопку навигации, чтобы изменить URL-адрес в адресной строке и отправить запрос на сервер. // protectDefault, чтобы предотвратить это и выполнить обработку для управления историей navigation.addEventListener ('click', e => {if (! e.target || e.target.nodeName! == 'A') return; // Метод pushState не изменяет URL-адрес адреса, но не запрашивает его history.pushState ({path}, null, path ); // AJAX-запрос по пути router (path);}); // когда веб-страница впервые загружена router ('/'); // DOMContentLoaded - это событие, которое происходит во время загрузки HTML и скрипта перед событием загрузки. (Поддерживает IE 9 и выше) // window.addEventListener ('DOMContentLoaded', router); // При нажатии на обновление текущая страница (например, loclahost: 5004 / service) запрашивается с сервера. // На стороне сервера должна быть возможность ответить. // ex) app.get ('/ service', (req, res) => res.sendFile (path.join (__dirname + '/public/data/service.html'))); } ());
Метод PJAX не отправляет новый запрос на сервер, и поэтому страница не обновляется. Однако, поскольку для каждой страницы существует уникальный URL-адрес, нет проблем с управлением историей. Также нет проблем с SEO, потому что вы не используете хеш.
Однако, если вы, например, нажмете кнопку обновления браузера, на сервер будет отправлен запрос, такой как loclahost: 5004 / service. В это время сервер должен ответить клиенту с помощью HTML в соответствии с URL.
Это смесь рендеринга сервера и AJAX . Сервер должен ответить на HTML, если Accept запроса хадера клиента имеет значение «text / html», и если акцептором хадера запроса является «application / json», только необходимые ресурсы должны отвечать JSON. Например, если в браузере сделан запрос с обновлением, то хадер принятия запроса будет иметь вид 'text / html, application / xhtml + xml, application / xml; q = 0,9, image / webp, И сервер отвечает на HTML. Для запросов AJAX, метод setRequestHeader Чтобы указать тип шахты данных, которые будут возвращены как json.
Пример реализации этого показан ниже.
// Запрос JSON с сервера req.open ('GET', url) // Запрос JSON с сервера req.open ('GET', url) if (req.readyState === XMLHttpRequest.DONE) {if (req.status === 200 ()); ) разрешить (req.response), иначе отклонить (req.statusText); } get ('/ service'). then (res => render (res)); // Сервер const express = require ('express'); const app = express (); const fs = require ('fs'); function () {res.sendFile (path.join (__dirname + (req, res) => res.format ({ '/ public / data / service /');}, // AJAX-запрос 'application / json': function () {res.send (JSON.parse (fs.readFileSync ('./ public / data / service). });}, 'default': function () {// зарегистрировать запрос и ответить 406 res.status (406) .send ('Not Acceptable'); . app.listen (3000, function () {console.log ('прослушивание http // localhost: 3000');
Чтобы запустить пример метода PJAX, выполните следующую команду.
$ npm run pjax
Мы рассмотрели процесс разработки SPA от традиционного метода связи до метода PJAX. Следующие четыре метода обобщены с точки зрения управления историей, SEO-отклика и взаимодействия с пользователем.
Классификация История Управление SEO ответ Пользовательский опыт Рендеринг сервера Сложность Реализация IE Соответствие традиционному методу связи ◯ ◯ ✗ ◯ Простой метод AJAX ✗ ✗ ◯ ✗ Обычно 7 или более Хеш-метод ◯ ✗ ◯ ✗ Обычно 8 или более PJAX-метод △ ◯ ◯ △ Сложный 10 или более
У всех архитектур программного обеспечения есть компромиссы. SPA также не является серебряной пулей для всех применений. Необходимо выбрать подходящий метод с учетом ситуации приложения.
Copyleft © 2017 . www.info-center.od.ua Информационный центр - Всегда в центре событий