Строим свой full-stack на javascript: клиент

Стуктура

Структура клиентской части зависит от выбранного фреймворка. В Contoso общей частью для клиентских реализаций (React/Vue) являются:


Сервисы (services) — модули доступа к данным через API. Если необходимо, тут происходят дополнительные преобразования данных.

Форматеры (formatters) — предоставляют форматирование данных на клиенте (дата, валюта, отображение имени, т.д.).

Помощники (helpers) — выполняют такие общие для клиента операции, как AJAX запросы, доступ к конфигурации, отображение всплывающих сообщение и подобное.

Помимо этого есть части, которые реализуются в зависимости от выбранного фреймворка:

Компоненты (components) — строительные блоки для клиентского интерфейса. Все современные фреймворки поддерживают создание UI как дерева компонентов, но конкретная реализация различается. Обычно компоненты группируются по папкам в зависимости от функционала (Project, UserAccount, Orders, e.g.).

SPA

SPA становится стандартом в современных веб приложениях, предоставляя лучший уровень удобства работы (user experience). Приложение загружает основные ресурсы (стили, код, разметку) при первой загрузке страницы, после этого последующие изменения состояния происходят на клиенте. Данные с сервера грузятся через AJAX реквесты, HTML рендерится на клиенте на основе HTML шаблонов или прямо из JS (React).

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

Веб приложения можно писать как набор нескольких SPA модулей или комбинируя с частями, работающими в классическом режиме постбэков (round trips). В Contoso страницы аутентификации работают через постбэки, все остальное приложение сделано как SPA.

Валидация

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

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

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

В Contoso, в React, для валидации не используется вспомогательных библиотек, для Vue используется пакет «vue-validator».

Немного истории

Веб стандарты меняются медленно: например, последняя версия HTML 5 была утверждена 2 года назад, а предыдущая XHTML впервые появилась в 2000, т.е. промежуток между новыми версиями был 14 лет. При этом, в новой версии ничего принципиально нового не появилось. CSS тоже развивается не быстро: в первый раз я услышал про flexbox layout в 2012, прошло 4 года, и вот, вроде бы можно его использовать, если вас устроит частичная поддержка в IE11, и ее отсутствие в более ранних версиях. Даже JavaScript меняется не так быстро как может показаться: перед недавним обновлением ES6, предыдущая мажорная версия 5.0 была выпущена целых 6 лет назад. При этом, появление нового стандарта не означает что его можно начать использовать сразу. Должно пройти долгое время, пока все основные браузеры начнут его поддерживать (а для IE можно не дождаться).


Тем не менее, требования к клиентским приложениям постоянно растут. Планку поднял Google, выпустив в далеком 2005 Гугл Карты и Gmail клиент — интерактивные SPA (Single Page Application) c невиданным до этого уровнем удобства использования. Такие приложения было сложно писать, и не каждая компания могла себе это позволить. Широкое распространение они получили относительно недавно (3-4 года назад).

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

Выбираем CSS фреймворк

CSS фреймворки предоставляют набор шаблонных стилей, которые позволяют облегчить позиционирование элементов (Grid System), переопределяют стандартные настройки стилей (HTML по умолчанию выглядит красиво) и предоставляют набор виджетов с включением JS, таких, как модальные окна, навигационное меню, выбор даты и т.д.

Основные опции:

Bootstrap — это основной выбор и первый массовый css фреймворк. Изначально создан Twitter, хорошо подходит как для десктопной так и для мобильной разработки. Большой набор компонентов. Из-за большой популярности сайт на bootstrap может выглядеть шаблонно.

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

Semantic UI — делает упор на хорошо продуманной семантике (именовании) классов элементов, не так хорош для мобильной разработки.

Material Design — это не фреймворк, а рекомендации по стилю от Google, которые используются в продуктах компании (Android). Есть реализации этого дизайна для React Material UI и для других JS фреймворков.

Дополнительно можно использовать библиотеку с набором иконок в виде шрифта, самая популярная из подобных Font Awesome.

В Contoso используются Font Awesome и Bootstrap.

Обработка ошибок

Основной источник ошибок на клиенте — это AJAX запросы. Если вы разрабатываете и клиентскую и серверную части, бывает проще сразу пересылать текст ошибки который можно показать на клиенте. Если API разрабатываются отдельно, то ошибка обрабатывается по HTTP коду или кастомному строчному коду, это лучше делать сразу в методе сервиса который делает AJAX запрос.

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

Тем не менее, в продакшине вы можете логировать ошибки с клиента, для этого есть несколько сервисов, например logentries.

Изучаем Derby 0.6, пример #1


Tutorial

Последние несколько месяцев я участвую в нескольких проектах, разрабатываемых на Derby (реактивный fullstack javascript-фреймворк). Часть из них вполне успешно работает в продакшине, часть стартует в ближайшее время. Пока я изучал данную технологию возникло несколько мыслей. Во-первых, информации о данной технологии крайне мало: документация скудна, а статьи, которые написаны (я про англоязычные), обычно пишутся людьми, потратившими от силы день-два на ее изучение. Во-вторых, есть небольшая группа людей, которые отлично знают данную технологию и используют ее в своих проектах, успешно решив все проблемы, о которые спотыкаются начинающие. Идея у меня проста — поделиться полученными знаниями, если это конечно будет интересно и востребовано. Я хочу взять несколько примеров из проекта derby-examples и разобрать их по полочкам. Либо, воссоздавая их с нуля, попутно объяснить логику создания, с точки зрения специалиста, либо же, по готовому примеру объяснить те моменты, которые были не раскрыты в предыдущих примерах. Короче, если понравится, думаю разобрать 5-6 примеров.

Derby.js — новый взгляд на веб-разработку

Из песочницы

Вы веб-разработчик? Для вас есть новости.

В чем собственно проблема?

Все веб-фреймворки можно разделить на группы. У каждой из этих групп есть свои достоинства и недостатки.Сервер-ориентированные Например: RoR, Django, Asp Net, Express.js Генерируют html на сервере. Такой подход хорош для статических страничек. Но как только вы хотите сделать что-то интерактивное, то начинаете утопать в jQuery-коде.Клиент-ориентированные Например: Backbone.js, Knockout.js, Ember.js, Batman.js Генерируют html прямо на клиенте из темплейтов. Код на клиенте структурирован. Хорошо для интерактивных сайтов. Не отменяет необходимость использовать сервер-ориентированный фреймворк, что ведет к дублированию кода (модели, валидация и т.д.)Кросс-компилируемые Например: GWT, Cappuccino Позволяют писать всё на одном языке. Очень большой уровень абстракции. Шаг вправо, шаг влево — расстрел. Так же ни один из фреймворков не имеет механизмов синхронизации данных между клиентом и сервером и оставляет реализацию этого на нашу совесть.

AJAX запросы

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

Если в проекте все равно для чего-то используется JQuery, то можно его использовать и для AJAX запросов. В Contoso JQuery обернут промисами и используется через модуль httpHelper.

Еще хорошая опция — axios

Эта библиотека имеет удобный API, поддерживает промисы, и что очень важно для full-stack разработки, работает одинаково как на сервере так и на клиенте

Использовать XMLHttpRequest напрямую из JS сложно, поэтому ему на замену предлагается нативный Fetch метод, он еще не поддерживается во всех основных браузерах, но как всегда в подобных случаях, есть библиотека с полифилом fetch polyfill, который работает с тем же API.

Существует несколько действий, которые можно выполнять при каждом AJAX запросе.

Можно блокировать UI, чтобы пользователь не мог совершить еще какие-то действия, пока AJAX запрос не отработает и состояние системы не обновится.

Если во время запроса возникла ошибка, по-умолчанию можно показывать общее сообщение (например «Server Error») для любого запроса.

Опционально можно добавить локальное логирование всех AJAX запросов в консоль браузера.

Упаковка

При написании большого клиентского приложения приходится разбивать его на много мелких модулей, при этом браузер плохо работает с большим количеством файлов (HTTP2 это исправит). Поэтому, есть практика клиентских билдов, когда несколько файлов, с кодом или стилями, объединяются в одну или несколько сборок (bundles). Для этого используются разные инструменты.

Менеджеры задач (task runners) предназначены для широкого спектра задач, таких как объединение и сжатие JS файлов, компиляция SASS/LESS, копирование, переименование файлов и многое другое. Необходимая работа разбивается на задачи, которые могут выполняться отдельно через CLI (консоль). Для выполнения разнообразных задач, используются плагины для конкретного менеджера. Самые популярные менеджеры задач Gulp и Grunt. Последнее время есть тенденция использовать инструменты npm для запуска скриптов и писать сами скрипты на Node без использования менеджера задач и его плагинов.


Сборщики модулей (module bundlers) помимо общих задач клиентской сборки, анализируют зависимости в JavaSrcript файлах и объединяют исходную сборку соответственно. Например, если у вас есть module1.js который импортирует module2.js, которые в свою очередь импортирует module3.js и lodash. То в исходной сборке модули будут в нужной последовательности (lodash, 3, 2, 1). Сборщики модулей по умолчанию ожидают увидеть внешние модули как npm пакеты в папке ‘node_modules’.

Самым популярным на сегодняшний момент является WebPack. Первым инструментом подобного типа был Browserify, он проще чем WebPack, но требует больше настройки. Набирает популярность Rollup — он отличается тем, что при использовании ES6 синтаксиса для импорта можно определить, какие части внешних библиотек используются и включать только их, что позволяет уменьшить размеры клиентской сборки.

В Contoso для клиентских сборок используется WebPack, для запуска задач — npm или непосредственно консоль. WebPack делает следующее: объединение модулей, транспиляцию TypeScript в ES5, преобразование стилей из LESS в CSS и объединение их в один файл, минификацию JS и CSS для продакшин сборки.

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

WebPack позволяет для некоторых JS фреймворков поддерживать режим HotReload — это когда происходит обновление JS и CSS без полной перегрузки страницы. Поддерживается в React и Vue, но сложно в настройке и иногда не отрабатывает корректно. В Contoso не используется.

Буду рад замечаниям комментариям.

Stay tuned!

Маршрутизация

При классической организации веб приложения, различные маршруты (URLs) загружают различные страницы с сервера. В SPA приложении все данные грузятся сразу. При переходе на другую страницу, загрузки новой страницы с сервера не происходит и URL остается таким же.

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

Есть несколько способов добавления маршрутизации в SPA:

  • С хэштегом — используется для поддержки старых браузеров (IE9), адрес выглядит как www.mysite.com/#/product/list
  • С HTML5 History API — адрес выглядит так же, как и в обычном веб приложении www.mysite.com/product/list

Для каждого клиентского фреймворка есть отдельная библиотека для маршрутизации.

В Contoso используется режим History API (pushState).

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


С этим читают