Это простое упражнение, в котором я пытаюсь создать базовый веб-сайт (SPA) без каких-либо сторонних зависимостей или инструментов сборки, но при условии, что мы находимся в мире, где все браузеры реализовали большинство современных предлагаемых API-интерфейсов Javascript. .

Вот что мы будем строить: (демо здесь)

Простой веб-сайт для поиска музыкальных подкастов, составления списка их выпусков и их прослушивания.

Основная идея состоит в том, чтобы использовать компонентный подход, поэтому, имея это в виду, я буду использовать следующую структуру для кода:

<root>
 + index.html
 + src
   - styles
   - js
     * api (here is the code to access podcasts services)
     * components (here are the views and partials for the UI)
     * config (here is the routing configuration)
     * plugins (support code)

Вы можете получить окончательный код здесь.

Первое, что я буду использовать, это Поддержка загрузки модулей Javascript в современных браузерах (Chrome и Safari).

Итак, HTML-файл будет выглядеть так:

Важной частью здесь является атрибут type для тега script.
Используя значение module, вы сообщаете браузеру использовать синтаксис javascript-модулей ES2015 с его API импорта / экспорта.

В разметке мы устанавливаем только корневую точку входа, в которую мы будем монтировать наше приложение из основного файла.

Главный файл приложения

Этот основной файл вставляет макет приложения (заголовок со счетчиком, который мы будем использовать для состояния загрузки) и делегирует отрисовку основного содержимого маршрутизатору приложения:

Мы просто создаем новый элемент html с макетом и настраиваем клиентский маршрутизатор с конфигурацией маршрутизации, корневым элементом, в котором он должен отображать основной контент (на основе URL-адреса), и слушателем, поэтому мы может реагировать на события загрузки (переход между страницами, когда данные не кешируются).

Маршрутизатор

Итак, маршрутизация на стороне клиента. Многие автоматически будут искать стороннюю библиотеку, потому что кажется, что это сложная область, но правда в том, что если вам просто нужно справиться с простой навигацией, построить маршрутизатор с нуля не так уж и сложно. Он просто синхронизирует ваш URL-адрес с компонентом.
Что касается данных, необходимых компоненту рендеринга, вы можете настроить компонент так, чтобы он загружался, или вы можете загрузить его через маршрутизатор и просто передать компоненту. Я предпочитаю второй способ, потому что я не хочу, чтобы новая страница начинала отрисовку, пока у меня не были ее данные, и я также не хочу обрабатывать состояние loading для каждой страницы (если вы создаете вспомогательный компонент для этого, я думаю, лучше просто использовать роутер).

Вот маршрутизатор приложений:

Это простой, но его достаточно для начала.
Я использовал этот подход в некоторых других проектах с немного более сложным маршрутизатором, и я обычно создаю другой файл для правильной обработки popstate и для экспорта некоторых вспомогательных функций navTo и redirectTo.

Итак, что мы делаем:

  • Слушайте события щелчка по ссылкам в документе
  • Прослушивание внешних изменений в URL-адресе (пример: кнопки назад и вперед в браузере)
  • Запускать обработку первого изменения URL-адреса при загрузке страницы

И когда нам нужно загрузить новую страницу:

  • На основе URL-адреса, к которому мы хотим перейти, найдите правильную конфигурацию маршрутизатора.
  • Как только он у нас есть, мы извлекаем параметры, которые могут понадобиться загрузчику данных из URL-адреса.
  • Вызвать загрузчик, чтобы получить необходимые данные
  • Обновление внутреннего состояния маршрутизатора: новый компонент представления и новые данные
  • Выполните повторную визуализацию основного элемента маршрутизатора с новым состоянием, создав экземпляр нового компонента с новыми данными и вызвав его функцию render, которая возвращает HTMLElement.
  • Восстановить позицию прокрутки

Также выполняется обратный вызов для отображения индикатора загрузки приложения.

Домашняя страница

Хорошо, теперь нам нужно узнать о страницах (главная, сведения о подкасте и сведения об эпизоде).
Мы только что видели эти страницы может иметь конструктор, который принимает исходные данные, и функцию render, которая должна возвращать HTMLElement с ожидаемым содержимым.

Поскольку все страницы будут иметь какое-то общее поведение, и нам понадобится несколько их экземпляров, мы создадим для них базовый класс, от которого будет унаследована каждая страница.

На наши страницы будет прикосновение Backbone и еще одно - React.
Давайте начнем с HomePage просмотр.

В конструкторе страницы мы получаем данные от маршрутизатора и вызываем родительский объект для сохранения состояния экземпляра. Мы будем использовать это состояние для повторного / рендеринга представления.

У нас также есть получатель, который возвращает карту событий DOM, которые страница должна обрабатывать. Синтаксис основан на Backbone.
Ключ для каждой записи состоит из частей: первая - это имя события DOM, которое мы хотим прослушать, а вторая часть, селектор для элемента, к которому мы хотим прикрепить слушателя; вторая часть - это функция, которую мы хотим связать с событием.
Фактически, прослушиватели событий не привязаны к элементам, представленным селектором, они все привязаны к корневому элементу для экземпляра представления. и есть управление делегированием в BasePage.

В процессе рендеринга используется функция html, которая должна возвращать строку, представляющую HTML, чтобы визуализировать страницу с ее предоставленным состоянием.
В router мы увидели, что генерировали содержимое для страницы, вызывающей функцию render, которая возвращает HTMLElement. Эта функция реализована в классе BasePage (мы сейчас к этому приступим).

Последняя важная вещь, на которую следует обратить внимание, - это статическая функция dataLoader, которая используется маршрутизатором, чтобы узнать, как загружать (асинхронно) данные, необходимые для этого представления.

Класс базовой страницы

Все идет нормально. Давайте посмотрим на базовый класс для каждой страницы приложения:

В конструкторе мы сохраняем данные, полученные от класса страницы, создаем корневой HTMLElement для этого представления и регистрируем возможные обработчики событий DOM, которые может определять подкласс.

Затем у нас есть только функция для обновления состояния экземпляра (которая повторно отображает экземпляр страницы) и функция визуализации для установки строки HTML, созданной подклассом в корневом экземпляре HTMLElement.

Частичные

Если вы вернетесь к коду рендеринга HomePage, вы заметите, что каждая информация подкаста рендерится с использованием партиала PodcastSummary.

Этот вид компонентов является более простым, поскольку ему не нужно обрабатывать навигацию и не требуется динамическое состояние.
Эти компоненты похожи на функциональные компоненты без сохранения состояния в Реагировать. Они просто получают данные для рендеринга и возвращают соответствующую строку HTML.

Остальной код

Код для других страниц очень похож на код, который вы только что видели в классе HomePage.
Есть и другие соответствующие файлы:

  • /src/api/ В этом файле хранятся вызовы AJAX к службам подкастов iTunes, а также запросы на получение файла сведений о подкасте RSS.
  • /src/plugins/< sizeslocal-cache.js Этот файл является прокси для API локального хранилища, который позволяет хранить значения с метаинформацией Time To Live.
  • /src/plugins/< sizesajax.js Этот файл представляет собой тонкую оболочку над API выборки, упрощающую его использование.

Вы можете просмотреть / скачать готовый код для этого домашнего проекта здесь, чтобы узнать их подробности.

Вывод

Как видите, в последних версиях браузеров вы можете довольно легко реализовать базовое решение без сторонних зависимостей.

Я уверен, что через год или два, когда все браузеры будут реализовывать эти API, появятся новые выдающиеся API, которые будут доступны только в одном или двух из них, поэтому мы все равно будем ждать, пока все браузеры будут реализовывать их, как и мы сейчас. Но ты знаешь? Вот что делает нашу жизнь интересной.

Если вы зашли так далеко, я хочу поблагодарить вас за то, что уделили мне немного времени, и я надеюсь, что вы нашли что-то полезное в этой статье.

Дополнительный бонус: я написал дополнительную статью, чтобы показать, насколько легко можно добавить разделение кода в это базовое приложение.
Оцените это! JS: Давай попробуем будущее сегодня (дополнительный бонус)