что такое single page application

Как написать одностраничное приложение (SPA) с использованием Vue.js

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

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

Ознакомившись с примером этого приложения, вы научитесь извлекать данные в Vue, создавать роуты и разберётесь с интересной особенностью Vue — однофайловыми компонентами.

Бэкенд

В этом руководстве мы в основном сосредоточимся на фронтенде на Vue. Размышлять о написании REST бэкенда мы не будем. Для примера будет использоваться сервис jsonplaceholder.typicode.com предостовляющий заглушку в виде REST API.

Фронтенд

Инструменты

Начать работу с Vue очень просто. С использованием правильных инструментов это ещё проще. Рекомендую взглянуть на проект vue-awesome, который содержит список инструментов, компонентов, библиотек и плагинов на все случаи жизни.

Vue-cli

При создании нового проекта рекомендуется воспользоваться Vue-cli. Так можно создавать проекты с использованием официальных шаблонных проектов Vue, или одного из множества шаблонных проектов с открытым исходным кодом, и, конечно же, вы можете создать свой собственный и использовать его в любом месте.

Итак, для начала установим vue-cli в качестве глобального пакета:

Затем проинициализируем проект с выбранным шаблоном; для нашего примера более чем достаточно использовать webpack-simple.

Далее перейдём в папку vue-spa и запустим npm install в терминале. После установки всех пакетов, мы можем запустить наше приложение в режиме разработки.

Эта команда автоматически запустит наш проект на локальном dev-сервере webpack. В браузере появится наше простейшее приложение Vue. Конечно оно выглядит совсем не так, как бы нам хотелось, и годится лишь в качестве отправной точки для начала чего-то большего. Чтобы продолжить работу, предлагаю сначала ознакомиться со структурой нашего шаблона.

Шаблон webpack-simple

Внутри шаблон webpack-simple имеет следующую структуру:

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Файл index.html содержит простую разметку HTML с единственным элементом “app” в body. Он будет заменён на DOM, сгенерированный vue. По этой причине тэг body не рекомендуется использовать в качестве корневого элемента.

Однофайловые компоненты

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

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

В настоящий момент нам абсолютно нечего отображать в нашей навигационной панели, поэтому давайте получим данные с сервера. Для этого я выбрал Axios — простой в использовании HTTP-клиент. Вы также можете использовать любой удобный для вас способ, например, Vue-ресурс или собственную выборку или даже jQuery Ajax.

Шаг 2

Затем импортируем его в компонент App и определим метод getAllPosts() который будет делать запрос к серверу и присваивать его свойству posts. Вызываем метод в хуке created(), который будет вызываться после создания экземпляра Vue и после установки настроек обращения к данным.

А теперь отобразим все заголовки записей в боковой панели.

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

Шаг 3

Для этого воспользуемся официальной Vue библиотекой vue-router. Как уже понятно из названия, библиотека позволяет настраивать роутинг для нашего приложения.
Установим библиотеку:

Для настройки роутинга вернёмся к файлу main.js. Здесь мы определим настройки роутинга и добавим их в наш экземпляр Vue.

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

Остался заключительный шаг. Нам нужно отобразить содержимое записи поста.

Шаг 4

Перейдём к файлу Post.vue, в котором добавим простой шаблон:

Затем нам нужно установить параметры экземпляра Vue для этого компонента. Здесь все также как в настройках отображения всех постов. Объявим опцию props с меняющимся id, которая будет получать номер нашего поста. Далее, объявим объект данных, как уже делали в App.vue:

Затем опишем метод getPost(), который будет получать только одну запись поста по идентификатору и вызовем его в хуке created().

Почти готово. Если мы запустим приложение сейчас, мы увидим, что, хотя URL-адрес меняется, мы видим единственный пост, который был отрисован первым. Дело в том, что для отрисовки разных постов у нас есть один и тот же компонент, и Vue не нужно его пересоздавать из-за лишней траты ресурсов, а это также означает, что хуки жизненного цикла компонента не будут вызваны.
Чтобы это исправить, нам просто нужно установить watcher для объекта $route.

Теперь всё работает так, как и должно. Чтобы получить версию для продакшина достаточно выполнить команду npm run build в консоли.

Источник

Что такое SPA или одностраничный портал

В этой статье речь пойдет о Single Page Application (SPA). Будут рассмотрены плюсы и минусы web-приложения построенного по принципам одностраничного сайта (SPA)

Что такое SPA

Single Page Application – сокращенно SPA, в переводе на русский язык означает “Приложение одной страницы”. Другими словами SPA – это web-приложение, размещенное на одной web-странице, которая для обеспечения работы загружает весь необходимый код вместе с загрузкой самой страницы. Приложение такого типа появились сравнительно недавно, с началом эры HTML5 и SPA является типичным представителем приложений на HTML5.

Если приложение достаточно сложное и содержит богатый функционал, как например, система электронного документооборота, то количество файлов со скриптами может достигать нескольких сотен, а то и тысяч. А “…загрузка всех скриптов…” никоим образом не означает, что при загрузке сайта будут загружены сразу все сотни и тысячи файлов со скриптами. Для решения проблемы загрузки большого количества скриптов в SPA призван API под названием AMD. AMD реализует возможность загрузки скриптов по требованию. То есть, если для “главной станицы” одностраничного портала потребовалось 3 скрипта, они будут загружены стразу перед стартом программы. А если пользователь кликнул на другую страницу одностраничного портала, например, “О программе”, то принцип AMD загрузит модуль (скрипт + разметка) только перед тем как перейти на эту страницу.

Получается немного скомкано: “Одна страница.. другая станица, третья страница… одностраничный портал”. Расставим все точки над “Ё”. Страница сайта, на котором размещены все ссылки на все CSS, и ссылки на скрипты, необходимые для работы SPA мы назовем “Web-страница”. Файл с такой странице обычно называется “index.html” (в ASP.NET MVC может быть index.cshtml или index.vbhtml или даже index.aspx) А страницы, которые переключает пользователь внутри одностраничного портала назовем “модули”.

Давайте рассмотрим плюсы и минуты данного подхода. Зачем всё это нужно и почему SPA так популярен?

SPA: Плюсы

SPA: Минусы

Если вы программируете на C#, то единственным минусом SPA является необходимость изучения JavaScript. Во всяком случае, других глобальных проблем мне выяснить не удалось.

Составляющие SPA

Принципы любого фреймворка (о них поговорим позже), который реализует парадигму SPA должны придерживаться следующих понятий и определений:

Шаблоны SPA (SPA templates)

Как вы уже наверное догадались, SPA – это абстрактное понятие. Это принцип архитектуры приложения. Давайте поговорим с чего начать при разработке сайта по принципам SPA.

Существует большое количество базовых библиотек (фреймворк – от английского слова framework – “основа, структура, каркас”), которые реализуют принцип Single Page Application. Что дают эти фреймворки:

Так как я уже много лет работаю на платформе NET, то я буду рассматривать шаблоны Single Page Application на основе ASP.NET. Давайте рассмотрим следующую сравнительную таблицу.

Сравнение возможностей шаблонов SPA

что такое single page application. spa template. что такое single page application фото. что такое single page application-spa template. картинка что такое single page application. картинка spa template. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

В таблице приведены наиболее распространённые шаблоны для как основа построения Single Page Application приложения. Обратите внимание, синим фоном выделены базовые кирпичики для построения полноценного фреймворка, таких как DurandalJS и HotTowel, которые выделены зеленым цветом.

Итак, следуя данным предоставленных в таблице вы можете создать Single Page Application приложение используя “голый” ASP.NET и KnockoutJS. Однако, реализацию работы с данными (DAL) вам придется писать самостоятельно, впрочем, как и управление навигацией и историей навигации в том числе.

С другой стороны, вы в праве выбрать Ember или BreezeJS, или даже AngularJS от Google как основу своего сайта или даже как основу своего собственного фреймворка, факт остается фактом, недостающие принципы составляющие концепцию SPA вам придется реализовывать самостоятельно.

Альтернативой предыдущему решению может послужить выбор уже готового полноценного фреймворка (помечены в таблице зеленым фоном). У каждого варианта существуют свои “плюсы” и “минусы”.

Заключение

Примеров приложений построенных по принципам Single Page Application очень много. Одним из самых мощных и общеизвестных является GMail – почтовый сервис компании Google.

Я же хочу привести, как пример, сервис чтения RSS-каналов SilverReader по одной простой причине: этот сервис сделан с использованием фреймворка DurandalJS. И именно этот фреймворк я использовал для построения своего сайта “Что значит имя”.

Источник

Single Page Application в облачном хранилище

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Мы уже писали о том, как наше облачное хранилище может быть использовано в качестве площадки для размещения статических сайтов (1 и 2). Сегодня мы расскажем о том, как на базе хранилища можно размещать современные cайты, в основе которых лежит популярный и актуальный в наши дни подход Single Page Application (SPA).

SPA как подход

Аббревиатура SPA расшифровывается как «single page application» («одностраничное приложение»). В узком смысле она употребляется для обозначения одностраничного сайта, непосредственно выполняемого на стороне клиента в браузере. В более широком смысле SPA (иногда также употребляется аббревиатура SPI — «single page interface») означает целый подход в веб-разработке, который в наши дни получает все более широкое распространение. В чем заключается смысл этого подхода и почему он становится всё более популярным?

Начнем несколько издалека. Сегодня самым распространенным типом сайтов являются, конечно же, динамические сайты, в которых генерация страниц осуществляется на стороне сервера. При всех очевидных удобствах для разработчиков (каждый запрос создаёт страницу с чистого листа), такие сайты порой представляют собой настоящую головную боль для клиентов. Вот далеко не полный список неудобств, с которыми им приходится сталкиваться:

Часто описанные проблемы решаются так: страницы всё ещё генерируются на стороне сервера, но на стороне клиента выполняются небольшие JavaScript-сценарии, с помощью которых можно, например, выполнить валидацию формы до того, как она будет отправлена на сервер. Решение на первый взгляд неплохое, но и у него есть недостатки:

Конечно, жить со всем этим можно. Но подход SPA во многих случаях оказывается гораздо более эффективным.

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

SPA представляет собой не сайт в классическом понимании, а приложение, которое исполняется на стороне клиента, в браузере. Поэтому с точки зрения пользователя проблем со скоростью работы почти не бывает даже при медленном или нестабильном соединении с Интернетом (например, при просмотре сайта с мобильного устройства). Высокая скорость работы обеспечивается ещё и благодаря тому, что с сервера на клиента приходит теперь не разметка, а данные (в основном в формате JSON), и размер их невелик.

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

BAAS (Backend as a Service)

Технологии, позволяющие отрисовывать интерфейс на стороне клиента и взаимодействовать с сервером только путём отправки запросов без перезагрузки страницы, существуют уже давно. Например, API XMLHttpRequest появился ещё в 2000 году. Но использование этих технологий было сопряжено с трудностями: нужно было переписывать бэкенд, реализующий функции авторизации запросов и доступа к данным.

Сегодня всё стало намного проще благодаря появлению многочисленных BaaS-сервисов. Аббревиатура BaaS означает Backend as a Service. BaaS-сервисы предоставляют разработчикам веб-приложений готовую серверную инфраструктуру (расположенную, как правило, в облаке). Благодаря им можно сэкономить время (а зачастую ещё и деньги) на написание серверного кода и сосредоточиться на совершенствовании самого приложения и развития его функциональности. С помощью BaaS можно подключать к приложению или сайту любой бэкенд с любым набором функций — достаточно просто добавить на страницу соответствующую библиотеку.

В качестве примера можно привести сервис MongoLab, позволяющий подключать к веб-приложениям и сайтам облачную базу данных MongoDB.

Ещё один интересный пример — FireBase. Этот сервис представляет собой облачную базу данных и API для realtime-приложений. С его помощью можно, в частности, организовать обмен данными между клиентом и сервером в режиме реального времени: достаточно просто подключить на страницу JavaScript-библиотеку и настроить события на изменения данных. На основе FireBase легко реализовать, например, чат или ленту пользовательской активности.

В числе интересных и заслуживающих внимания BaaS-сервисов стоит также выделить:

С кратким обзором ВааS-сервисов можно также ознакомиться здесь.

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

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

Затем его нужно будет указывать в качестве значения заголовка X-Auth-Token во всех запросах работы с данными. Ответы на эти запросы будут включать заголовок Access-Control-Allow-Origin со значением «*», дающий возможность обращаться к хранилищу с любого внешнего домена.

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

HTML5 History API

Каждая страница динамического сайта имеет cобственный URL. SPA же устроены так, что в них можно изменять состояние страницы, не изменяя при этом URL. Но в таком случае возникает проблема: пользователь не сможет сохранить ссылку и затем вернуться к посещённому ранее разделу. Решить её можно несколькими способами.

Во-первых, можно использовать хэш-фрагмент (часть ссылки, которая идёт после символа «#»). В хэш-фрагмент помещается «виртуальный» адрес страницы, по которому можно восстановить прежнее состояние. Если пользователь перезагрузит страницу, то код на JavaScript, прочитав значение хэша, загрузит нужные данные и отобразит соответствующий ему раздел. Этот вариант используется на многих сайтах, но следует отметить, что подобные URL (например, «example.com/base/#!/section1/page2») выглядят не очень естественно. Кроме того, они состоят из двух сущностей: «реальный» адрес, по которому страница запрашивается у веб-сервера и «виртуальный», который обозначает логический раздел.

Во-вторых, можно воспользоваться HTML5 History API (см. также статью на Хабре). History API позволяет полностью изменять URL страницы в пределах текущего домена без её перезагрузки. В браузерах, поддерживающих этот API, нет никаких отличий между URL, использованных при загрузке страницы и URL, заданным из JavaScript.

Вызовы функций History API также связаны с нативными браузерными кнопками «Вперёд» и «Назад» и историей: при нажатии кнопки «Назад» браузер вызовет событие popstate, которое можно обработать в JavaScript-коде и отобразить предыдущий раздел. Посмотреть, как это работает, можно, например, здесь.

Чтобы сайт использовал описанную схему URL, нужно соответствующим образом настроить веб-сервер. Необходимо сделать так, чтобы вместо несуществующих страниц отдавалась главная страница SPA — обычно это index.html. В этом случае браузер клиента загрузит JavaScript, который уже отрисует нужный файл сайта исходя из текущего адреса страницы.

В настройках nginx это прописывается так:

Если SPA размещается на сервисах типа GitHub Pages или нашего хранилища, то настройки нужно устанавливать через панель управления.

Поисковая оптимизация

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

Одним из способов решения этой проблемы заключается в следующем: генерируются снапшоты страниц специально для ботов. Генерировать страницы следует по-разному в зависимости от того, используется ли HTML5 History API или нет.

В случае использования хэш-фрагментов Google и Yandex предлагают один и тот же подход: для разделения «реального» и «виртуального» адресов следует использовать «#!» вместо «#»; поисковые боты, увидев ссылку с таким разделителем, будут использовать специальный query-параметр «_escaped_fragment_» в URL и помещать в него часть адреса, следующую после после хэша.

Веб-сервер должен специальным образом обрабатывать такие запросы и отдавать полноценные HTML-страницы, чтобы боты могли их проиндексировать. (Потребность в этом возникает вследствие того, что часть URL после «#» по стандарту не является часть HTTP-запроса.) Рекомендуется также использовать специальный метатег (см. по ссылкам выше) т.к. некоторые страницы могут не содержать ссылок с «#!».

Если же для адресации используются полноценные URL, то сделать сайт индексируемым ещё проще: достаточно генерировать соответствующую заданному адресу страницу на сервере. Бот, посетив сайт, получит страницу c содержимым и успешно её проиндексирует.

Если сайт открыт в браузере, то страница сначала будет отображена в соответствии с HTML-разметкой, а затем уже в дело вступит JavaScript; при дальнейших переходах по ссылкам страница перезагружаться не будет. Такое поведение можно реализовать, перехватывая события нажатия на ссылки и отменяя их нативное поведение. В таком случае, бот перейдет по ссылке (атрибут «href»), а мы сможем контролировать все переходы, обрабатывая событие так, как нам нужно.

Отметим также, что такой подход позволяет оптимизировать скорость загрузки сайта: современные браузеры намного быстрее отрисовывают приходящий с сервера HTML, а загрузка JavaScript-сценариев, их парсинг и отрисовка страницы через DOM занимает обычно больше времени.

Как сгенерировать HTML-страницу, имея в наличии только JavaScript-код, работающий напрямую с DOM? Это можно сделать несколькими способами.

Во-первых, можно установить PhantomJS (это так называемый «headless» браузер на основе WebKit, управляемый через API) и настроить его на генерацию снапшотов страниц (см. практический пример здесь).

Во-вторых, можно воспользоваться инструментом prerender или сервисом Prerenderer.io, которые основаны также на PhantomJS.

В-третьих, в некоторых современных фреймворках (например, DerbyJS и React) эта функция уже реализована (см. также пример здесь: react-server-example).

Размещение SPA

В современном Интернете есть немало площадок, на которых можно размещать SPA. Рассмотрим наиболее популярные из них.

Dropbox

Самый простой вариант — размещение SPA в Dropbox. Для этого нужно создать в публичном каталоге Dropbox каталог для SPA, поместить в него все необходимые файлы, а затем выделить файл index.html и вызвать контекстное меню, нажав на правую кнопку мыши. В меню нужно выбрать пункт «Dropbox → Поделиться ссылкой». После этого будет создана ссылка и скопирована скопирована в буфер обмена, по которой приложение будет доступно через браузер.
В последнее время предпринимаются попытки расширить возможности по размещению сайтов в Dropbox (об этом можно почитать, например, здесь). Однако главной проблемы они не решают: в Dropbox имеются довольно жёсткие (вплоть до полной блокировки) ограничения трафика по публичным ссылкам. Поэтому описываемый вариант можно рекомендовать разве что для демонстрации SPA друзьям и коллегам.

GitHub Pages

GitHub Pages представляет собой бесплатную площадку для размещения статических сайтов и SPA. Чтобы разместить SPA на GitHub Pages, нужно создать для него репозиторий на GitHub и поместить статические файлы в ветку gh-pages для Project Pages или в ветку master для User/Organization Pages. Все вносимые изменения нужно будет коммитить в этот репозиторий. Более подробная справка доступна в официальной документации.

Bit Balloon

Bit Balloon — сервис новый, но уже достаточно популярный.
Каждому зарегистрированному пользователю бесплатно предоставляется 10 Мб дискового пространства. Чтобы разместить на нём SPA, достаточно просто загрузить все необходимые файлы. Сервис автоматически минифицирует JS, HTML и CSS, а также подключит CDN.

Премиум-аккаунт стоит 5$ в месяц. Для владельцев платных аккаунтов доступны различные дополнительные возможности — в частности, прикрепление собственных доментов и подключение разнообразных внешних сервисов.

Selectel Storage

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Это позволяет использовать полноценные URL (см. раздел о History API).

К несомненным преимуществам нашего хранилища перед другими площадками (в том числе и перед описанными выше) относятся низкая стоимость и гибкая система оплаты (никаких фиксированных тарифов; оплата взимается только за объём хранимых данных и исходящий трафик — посчитать можно здесь), а также наличие CDN и гибкой системы настроек параметров контейнеров и отдаваемых файлов.

Заключение

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

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

Источник

Фреймворк-независимое браузерное SPA

1. Но. зачем?

Существует огромное количество фреймворков для разработки SPA (Single Page Application).

Существует огромное количество документации, иллюстрирующей как создавать приложение на базе конкретного фреймворка.

Однако всякая подобная документация ставит фреймворк во главу угла. Тем самым превращая фреймворк из детали реализации в определяющий фактор. Таким образом значительная часть кода пишется не для удовлетворения нужд бизнеса а для удовлетворения нужд фреймворка.

Учитывая насколько hype-driven является разработка софта в наше время, можно быть уверенным в том что через несколько лет будут существовать новые модные фреймворки для фронтенд разработки. В момент когда фреймворк на базе которого построено приложение выходит из моды — вы вынуждены либо поддерживать устаревшую (legacy) кодовую базу либо стартовать процесс перевода приложения на новый фреймворк.

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

Данная статья является примером построения SPA с использованием высокоуровневых принципов дизайна архитектуры. При этом конкретные библиотеки и фреймворки выбираются для удовлетворения ответственностей, определённых желаемой архитектурой.

2. Архитектурные цели и ограничения

Цели:

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

Стимулируется разделение ответственностей (separation of concerns) и следовательно модульность кода так что:

Модули легко поддаются тестированию

Интеграции с внешними сервисами (boundaries) а также грязные хаки и воркэраунды вынесены в отдельные модули и не протянуты через несколько различных файлов. Таким образом смена реализации интеграции с сервисом или отказ от хака становится реалистичной задачей а не «долгосрочным рефакторингом»

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

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

Механики взаимодействия модулей не приводят к недопустимым проблемам с производительностью.

Зависимости от библиотек не торчат сквозь всю кодовую базу. Они удерживаются внутри ограниченного числа модулей, ответственных за интеграцию с конкретной библиотекой.

Ограничения:

Приложение должно работать в браузере. Следовательно оно должно быть написано с использованием (или скомпилировано в) HTML+CSS для определения статического интерфейса и JavaScript для добавления динамического поведения.

3. Ограничим тему данной статьи

Существует большое количество архитектурных подходов к структурированию кода. Наиболее распространенные на данный момент: слоеная (layered), луковичная (onion) и шестигранная (hexagonal). Беглое сравнение было дано в моей предыдущей статье.

Данная статья ограничивается слоем представления в терминологии слоеной/луковичной архитектур поскольку большинство SPA занимается исключительно отображением данных. Таким образом слои домена (domain) и приложения (application) могут быть проигнорированы. Как следствие, наиболее естественный способ понять назначение такого приложения — получить обзорное представление о слое представления.

Тем не менее данная статья определяет механики взаимодействия со слоями приложения и домена, в случае когда такие слои присутствуют.

Интересно отметить что в случае отсутствия вышеупомянутых слоев приложение напоминает классическую шестигранную структуру (также называемую Ports and Adapters) в которой представление является приложением. Взгляните на интеграцию с localStorage в TodoMVC примере созданном в качестве иллюстрации к данной статье (папка boundaries/local-storage).

4. Структура файлов. Как заставить SPA кричать?

Рассмотрим типичный онлайн магазин. Приблизительно так он мог бы быть нарисован на салфетке владельцем бизнеса:

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 1: типичный онлайн магазин, нарисованный на салфетке

Каким может быть наиболее кричащий способ структурировать кодовую базу? На рисунке 2 все страницы отражены как папки.

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 2: структура папок верхнего уровня, отражающая страницы определённые на рисунке 1

Заметим что мы добавили папку ‘shared’ как место где будут определены общие UI блоки, такие как шаблон, панель навигации, корзина.

Наши страницы построены из логических (и видимых) частей. Пока что назовем их ‘блоками’ и положим в папку с именем ‘parts’. Посмотрим что получилось (рисунок 3).

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 3: размещение вложенных блоков внутри подпапки ‘parts’

Как видно, вложенность выглядит отвратительно уже для второго уровня для страницы ’goods catalogue’. Путь ‘goods-catalogue/parts/goods-list/parts/good-details.js’ уже на границе адекватной длины пути к файлу. При том что в реальных приложениях два уровня вложенности — далеко не предел.

Давайте избавимся от папок «parts» в файловой структуре. Посмотрим на рисунок 4.

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 4: вложенные блоки вынесены из папок ‘parts’

Теперь внутри пути ‘goods-catalogue/goods-list’ находится три файла. goods-list.js (родительский) — расположен между файлами, определяющими вложенные в него блоки. В реальных проектах, учитывая кол-во разнородных файлов (js, html, css) это приводит к невозможности разделить файлы, определяющие текущий блок и файлы, отвечающими за вложенные в него блоки.

Если конкретный блок определяется несколькими файлам — создаем для него папку.

goods-list является блоком и состоит из более чем одного файла, потому для него создана папка.

filters является блоком состоящим из одного файла, потому для него не создана отдельная папка.

Если конкретный блок (неважно из одного файла или из нескольких) является вложенным блоком — добавим к названию файла префикс «. Таким образом все вложенные блоки будут подняты к верху папки в файловом обозревателе.

_goods-list folder является вложенным блоком относительно goods-catalogue соответственно к названию папки добавлен префикс.

goods-list.js является частью определения блока _goods-list соответственно префикс не добавлен.

_good-details.js является вложенным блоком относительно _goods-list соответственно префикс добавлен.

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 5: использование префикса «_» для разделения вложенных блоков от их родителей

Готово! Теперь открывая папку с блоком мы можем сразу же увидеть и открыть основной файл, определяющий данный блок. После чего при необходимости перейти к вложенному блоку. Обратите внимание что папка pages была переименована в components на рисунке 5. Так сделано поскольку страницы и блоки логически являются разными вещами но в терминологии HTML и то и другое может бы представлено как component. С этого момента папка components является основной папкой нашего приложения, «домом» для слоя представления.

5. Язык разработки. JavaScript?

Единственный язык который может быть выполнен в браузере это JavaScript. Существует множество статей посвященных его несуразности. Вы можете посмеяться о нем (тайм код 1-20), но это только веселая часть.

Важнее то, что новые возможности постоянно добавляются к языку. Спецификация обновляется каждый год. Новый фичи проходят 4-этапный процесс ревью перед тем как попасть в спецификацию. Однако зачастую, они реализуются браузерами ещё до прохождения через все 4 этапа. И довольно часто сообщество и авторы библиотек начинают использовать определенные фичи до того как они попадают в спецификацию. К примеру, декораторы стали широко применяться в 2015 году, но до сих пор не являются частью спецификации. С другой стороны, зачастую бизнес требует работоспособности приложения в устаревших браузерах, которые априори не поддерживают новых языковых возможностей.

Потому даже при использовании чистого JavaScript разработчик вынужден использовать транспилятор (babel) с тем чтобы получить JavaScript, совместимый с браузерами из «современного и модного» JavaScript. Поскольку использование транспилятора и соответствующее замедление сборки приложения неизбежно — нет причин игнорировать другие, более предсказуемые и более функциональные языки программирования.

Глубокий анализ возможных опций лежит вне границ данной статьи, но мой персональный выбор — TypeScript поскольку он:

Обеспечивает проверку типов на этапе компиляции

Будучи над-множеством JavaScript, может выполнять JavaScript код без дополнительного интеграционного кода

Определения типов (typings) могут быть добавлены поверх существующего JavaScript кода без его изменения. Благодаря простоте этой возможности, большинство существующих npm пакетов уже покрыты тайпингами. Таким образом вы можете использовать эти пакеты так, как будто бы они являются TypeScript пакетами. Соответственно их использование также является типо-безопасным.

6. Требования к дизайну приложения

Давайте вспомним ограничения, накладываемые браузерами: HTML, CSS, JavaScript. Также вспомним структуру файлов, определенную в разделе 4: дерево директорий, отражающее дерево визуальных элементов.

Таким образом первой целью [6.1] будет возможность определения компонентов средствами HTML и CSS и их последующее переиспользование другими компонентами.

Существенный недостаток чистого HTML состоит в том что он не типизирован. Существует достаточное количество движков шаблонизации, таких как underscore.js, handlebars.js. Однако все они принимают на вход строки, что ограничивает нас в проверке корректности используемых в шаблоне данных на этапе компиляции приложения.

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

Каждый UI элемент может выглядеть по разному в зависимости от переданных ему данных. HTML элементы принимают данные в виде HTML атрибутов. Этого достаточно для статической разметки. Для динамически изменяемой разметки нам необходимы некоторые хранилища для данных. В этих хранилищах данные будут изменяться в зависимости от действий пользователя на странице. В то же время, мы не должны потерять возможность передавать данные в компоненты в виде атрибутов.

Таким образом третьей целью [6.3] является возможность компонентов принимать данные из атрибутов и из хранилищ одновременно. Компоненты должны быть перерисованы при изменении любой части принимаемых данных.

Четвертой целью [6.4] станет определение требований к таким хранилищам:

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

Должна существовать возможность создавать хранилища вместе с конкретным компонентом. Это необходимо для случая когда различные экземпляры компонента автономны друг от друга и должны иметь различные наборы данных.

Хранилища должны иметь возможность использовать сервисы и функции слоев Domain и Application. Во избежание сильной связности между хранилищем и границами приложения, сервисы должны быть использованы с помощью механизма Dependency Injection. Хранилища должны ссылаться только на интерфейсы.

И последнее — мы не хотим чтобы данные внутри хранилищ были публичными во избежание нежелательного изменения данных в процессе рендеринга. Хранилища должны быть ответственны за свою целостность. Компоненты же, в свою очередь, должны быть ничем большим чем строго-типизированные-и-оптимизированные-html-шаблоны. Для достижения подобного разделения хранилища должны инкапсулировать данные внутри себя и предоставлять методы для работы с этими данными. Другими словами, хранилища должны быть классическими экземплярами классов.

Однако, как замечено ранее, хранилище может быть использовано большим количеством мелких компонентов. Было бы полезно определять срез данных, используемый конкретным компонентом. Таким образом мы достигнем:

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

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

Таким образом, пятая цель [6.5] — позволить хранилищам данных быть определенными как классические TypeScript классы. Обозначить механику определения среза данных, используемого конкретным компонентом.

Держа эти цели в голове, давайте перечислим необходимые логические блоки кода:

Компоненты (Components) — строго типизированные HTML шаблоны + CSS стили

Модели вида (ViewModels) — классы, инкапсулирующие состояние данных, используемое компонентом (и всей иерархией компонентов под ним).

Фасады моделей вида (ViewModel facades) — ограничивают видимость свойств модели вида теми, которые используются в конкретном компоненте.

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 6: желаемая структура кода в слое представления

Не-пунктирные стрелки отражают рендеринг компонентов родительскими компонентами. Направление стрелки отражает направление передачи атрибутов.

Пунктирные линии отражают зависимости одних логических кусков кода от других (ссылки).

Блоки с зеленой рамкой — границы модуля. Каждый модуль/подмодуль отражен выделенной под него папкой. Общие модули лежат в папке «shared».

Голубые блоки — модели вида. Модели вида определены по штуке на модуль/подмодуль.

Что упущено? Заметьте как модели вида на рисунке 6 не имеют никаких параметров. Это всегда справедливо для модулей верхнего уровня (страниц) и глобальных моделей вида. Но подмодули зачастую зависят от параметров, определённых в процессе работы с приложением.

Обозначим шестую цель [6.6] — позволить атрибутам подмодуля быть использованными моделью вида этого подмодуля.

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 7: атрибуты передаются не только в корневой компонент модуля но и в его модель вида

7. Техническая реализация

Я буду использовать популярные библиотеки с тем чтобы сделать статью легче для восприятия. По этой причине тут не будет сравнения различных вариантов — для реализации конкретной ответственности всегда будет использована наиболее популярная библиотека.

7.1. Компоненты

Для отрисовки строго-типизированной разметки можно использовать синтаксис tsx (типизированный jsx). Рендеринг tsx поддерживается различными библиотеками, такими как React, Preact and Inferno. Tsx НЕ является чистым HTML, тем не менее он может быть автоматически сконвертирован в/из HTML. Потому зависимость от tsx мне кажется допустимой т.к. в случае миграции на чистый HTML, значительная часть работы может быть выполнена автоматически.

Для уменьшения зависимости от конкретной библиотеки для рендера, мы ограничим компоненты так, чтобы они были чистыми функциями, принимающими атрибуты и возвращающими JSX узел. Этот подход был популярен и отлично себя зарекомендовал в ранний период развития React.

Хинт: в последние годы функциональные компоненты в виде чистых функций вышли из моды в сообществе React. Использование react hooks наделяет функциональные реакт компоненты сайд-еффектами и поощряет смешивание рендера с логикой управления состоянием. Хуки являются специфическим API для React и не должны использоваться при разработке в подходе, описанном в данной статье.

Другими словами, компоненты лишены состояния. Представим их через выражение UI=F(S) где

UI — видимая разметка

F — определение компонента

S — текущее значение данных внутри модели вида (здесь и далее — вьюмодели)

Пример компонента может выглядет так:

Этот компонент отвечает за отрисовку одного todo элемента внутри TodoMVC приложения.

Единственная зависимость в этом коде — это зависимость от синтаксиса JSX. Соответственно этот компонент может быть отрисован различными библиотеками. С таким подходом замена библиотеки для отрисовки все еще не является бесплатной, но является «реалистичной».

Итого мы достигли целей [6.1] и [6.2].

7.2. Модели Вида (вьюмодели)

Как было сказано ранее, мы хотим чтобы вьюмодели были написаны в виде TypeScript классов с тем что-бы:

Обеспечивать инкапсуляцию данных.

Предоставлять возможность взаимодействия со слоями domain/application посредством механизма dependency injection.

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

Применим принципы реактивного интерфейса (reactive UI). Подробное описание этих принципов приведено в этом документе. Данный подход был впервые представлен в WPF (C#) и назван Model-View-ViewModel. В JavaScript сообществе, объекты предоставляющие доступ к обозреваемым (observable) данным чаще называются хранилищами (stores) следуя терминологии flux. Отмечу что хранилище это очень абстрактный термин, он может определять:

Глобальное хранилище данных для всего приложения.

Доменный объект, инкапсулирующий логику логику бизнеса и не привязанный к конкретному компоненту но и не являющийся глобальным.

Локальное хранилище данных для конкретного компонента или иерархии компонентов.

Таким образом любая вьюмодель является хранилищем, но не каждое хранилище является вьюмоделью.

Определим ограничения к реализации вьюмоделей:

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

Вьюмодель не должна ссылаться на компоненты и не должна знать о том что существуют конкретные компоненты, ссылающиеся на эту вьюмодель.

Я использую mobx декораторы для того, чтобы сделать поля класса обозреваемыми. Пример вьюмодели:

Обратите внимание что мы ссылаемся на mobx напрямую, однако декораторы не присутствуют в теле методов.

Я продемонстрирую как абстрагировать реактивность и убрать зависимость от mobx в следующей статье. Пока что посчитаем достаточным обращаться к декораторам через пространство имен mobx. При таком ограничении заменить декораторы на декораторы из другой библиотеки можно будет с помощью автоматизированного скрипта.

Также обратите внимание что конструктор вьюмодели принимает первый аргумент типа . Это позволяет удовлетворить цели [6.6]. Тип должен совпадать с типом определяющим атрибуты корневого компонента модуля. Ниже обобщенный интерфейс вьюмодели:

Все методы вьюмодели необязательны. Они могут быть определены для:

Выполнения кода при создании вьюмодели

Выполнения кода при удалении вьюмодели

Выполнения кода при изменении атрибутов (под-)модуля.

В отличии от компонентов, вьюмодель хранит свое состояние (является statefull). Она должна быть создана когда модуль появляется на странице и удалена как только модуль исчезает со страницы.

Как показано на рисунке 7, точкой входа для модуля является его корневой компонент. Таким образом вьюмодель должна быть создана когда корневой компонент модуля добавлен в структуру DOM(mounted) и удалена когда он удаляется со страницы(unmounted). Решить эту задачу можно с помощью техники компонентов высшего порядка (higher order components).

Определим тип функции:

Эта функция возвращает компонент высшего порядка над moduleRootComponent, который:

Должен обеспечить создание вьюмодели перед созданием и монтированием (mount) компонента.

Должен обеспечить зачистку(удаление) вьюмодели при демонтировании (unmount).

Просмотрите реализацию использованную для приведенного в качестве примера TodoMVC приложения. Эта реализация немного более сложна т.к. учитываем возможность использования автономного IoC контейнера, что выходит за рамки данной статьи.

Пример использования данной функции:

Важно что реализация создания вьюмодели, а также доступности вьюмодели для дочерних компонентов, скрыта внутри withVM.

TodoMVCDisconnected компонент не зависит от библиотеки рендера

TodoMVC компонент может быть прорендерен в компоненте, не зависящем от библиотеки рендера

TodosVM ссылается только на декораторы. Потому, как описано выше, её отвязка от mobx реальна.

Хинт: в реализации из примера, функция withVM зависит от react context API. Вы можете попробовать реализовать аналогичное поведение в обход контекст апи. Важно, что реализация должна быть синхронизирована с реализацией доступа к вьюмодели из фасадов вьюмоделей — смотрите описание функции connectFn в следующем разделе.

7.3. Фасады вьюмоделей

Фасад определяет класс, выставляющий для публичного доступа ограниченное количество функций/данных модуля. Из рисунка 6 видно что мы хотим иметь по фасаду на каждый компонент. Однако создание дополнительного класса на каждый компонент будет излишне многословным.

Попробуем вместо классических «фасадов» использовать функции, принимающие вьюмодель (или несколько вьюмоделей) и возвращающие набор функций/данных, необходимых конкретному компоненту. Назовем их функциями среза (slicing function). Что если такая функция будет получать атрибуты компонента, который она обслуживает, в качестве последнего аргумента?

что такое single page application. image loader. что такое single page application фото. что такое single page application-image loader. картинка что такое single page application. картинка image loader. Одностраничные приложения (SPA) имеют мнжество преимуществ, таких как скорость, по-настоящему хороший UX, и полный контроль HTML-разметки. Становится всё больше и больше сайтов SPA; всё больше инструментов, которые упрощают процесс разработки SPA. Вы, вероятно уже читали о молодом и перспективном фреймворке Vue.js. Предлагаю вам глубже погрузиться в Vue и на конкретном примере разобраться с простым SPA.

Рисунок 8: передача атрибутов компонента фасаду вьюмодели (функции среза/slicing function)

Посмотрим на синтаксис (в случае одной вьюмодели):

Заметка: Я назвал аргумент функции, содержащий атрибуты компонента ‘OwnProps’ что-бы приблизить его к терминологии применяемой в react/redux.

Отрисовка такового компонента в списке todo элементов:

Заметьте что connectFn скрывает детали реализации реактивности:

Она берёт компонент TodoItemDisconnected и функцию среза sliceTodosVMProps — обе не знающие ничего о реактивности и о библиотеке для рендеринга JSX.

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

Смотрите на реализацию функции connectFn для TodoMVC приложения, сделанного в качестве примера.

8. Заключение

Итого весь код, относящийся к конкретным бизнес задачам приложения, независим от фреймворков. TypeScript объекты, функции, TSX — это все к чему мы привязаны.

Надеюсь что прочтение этой статьи продемонстрировало пользу проработки архитектуры SPA приложения вперёд старта разработки. Буду счастлив если майндсет хотя бы одного разработчика на старте разработки SPA изменится с «берем свежий фреймворк и все должно быть хорошо» на «подумаем что конкретно нужно сделать и выберем подходящие инструменты».

Все же, может ли слой представления быть полностью независим от фреймворков в реальном приложении?

Для того что-бы убрать ссылки на mobx, react и mobx-react из слоя представления, нужно сделать немного больше:

Абстрагироваться от mobx декораторов

Абстрагировать все фреймворко-зависимые библиотеки, используемые слоем представления. К примеру TodoMVC зависит от библиотек react-router и react-router-dom.

Абстрагироваться от синтетических событий, специфичных для конкретной библиотеки, отрисовывающей JSX.

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

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

P.S. Сравнение рассмотренной структуры и ее реализации с популярными фреймворками для разработки SPA:

В сравнении со связкой React/Redux: вьюмодели заменяют reducers, action creators и middlewares. Вьюмодели содержат состояние (являются stateful). Нет time-travel. Множество хранилищ. Отсутствие просадки производительности вызванной наличием большого числа использований функции connect с какой то логикой внутри. Redux-dirven приложения становятся все медленнее и медленнее с течением времени из за добавления новых connected компонентов в приложение. При этом не существует какого то конкретного ботлнека, устранением которого можно было бы исправить ситуацию.

В сравнении с vue: строго типизированные представления благодаря TSX. Вьюмодели являются обычными классами и не требуют использования функций сторонних библиотек, равно как не обязаны удовлетворять интерфейсу, определенному сторонними фреймворками. Vue.js заставляет определять состояние внутри определенной структуры имеющей свойства ‘data’,’methods’, и т.д. Отсутствие vue-специфических директив и синтаксиса привязки к модели.

В сравнении с angular: строго типизированные представления благодаря TSX. Отсутствие angular-специфических директив и синтаксиса привязки к модели. Инкапсуляция данных внутри вьюмоделей в отсутствие двусторонней привязки данных (two-way data binding). Хинт: для определенных сценариев, таких как формы, двусторонняя привязка данных удобна и полезна.

В сравнении с чистым react где управление состоянием выполняется с помощью хуков (hooks, такие как useState/useContext):Лучшее разделение ответственностей. Вьюмодели могут восприниматься в терминологии реакта как контейнер компоненты, которые лишены возможность рендерить что-либо и являются ответственными исключительно за работу с данными. Нет необходимости:

следить за последовательностью вызова хуков.

отслеживать зависимости хуков useEffect внутри ‘deps’ массива.

проверять смонтирован ли все еще компонент после каждого асинхронного действия.

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

Как любая технология, хуки (и в частности — useEffect) требует разработчика следовать некоторым рекомендациям. Эти рекомендации не являются частью интерфейсов, но приняты как «подход», «модель мышления (mental model)» или «стандартные практики (best practices)». Прекрасная статья про использование хуков от члена команды разработки react. Прочитайте ее и ответьте себе на два вопроса:

Что вы получаете используя хуки?

Как много правил, не контролируемых компилятором/линтером и сложно отслеживаемых на ручном код ревью, нужно соблюдать чтобы использование хуков оставалось предсказуемым?Если второй список получился больше первого — это хороший сигнал что относится к хукам стоит с большой осторожностью. Хинт: пример фрустрации от хуков

В сравнении с react-mobx интеграцией. Структура кода не определяется пакетом react-mobx и не предлагается документацией к нему. Разработчик должен придумать подход к структурированию кода сам. Рассмотренную в статье структуру можно считать таким подходом.

В сравнении с mobx-state-tree: Вьюмодели являются обычными классами и не требуют использования функций сторонних библиотек, равно как не обязаны удовлетворять интерфейсу, определенному сторонними фреймворками. Определение типа внутри mobx-state-tree опирается на специфические функции этого пакета. Использование mobx-state-tree в связке с TypeScript провоцирует дублирование информации — поля типа объявляются как отдельный TypeScript интерфейс но при этом обязаны быть перечислены в объекте, используемом для определения типа.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *