24 November 2013

Изоморфный JavaScript — будущее веб-приложений

Website developmentJavaScript
Original author: Spike Brehm
В компании Airbnb мы многому научились за последние несколько лет, создавая мощные веб-приложния. Мы погрузились в мир одностраничных приложений в 2011 г., делая мобильную версию нашего сайта, с тех пор, кроме прочего, мы запустили Wish Lists и новый поиск. Все это — большие JavaScript приложения, что означает то, что тонны кода запускаются в браузере, чтобы обеспечить современный интерактивный пользовательский опыт.

Это обычный подход сегодня, когда такие библиотеки, как backbone.js, ember.js и angular.js помогают разработчикам создавать мощные JavaScript приложения. Мы поняли, однако, что такие приложения имеют несколько критических ограничений. Чтобы стало понятно, давайте предпримем небольшой тур по истории веб-приложений.

Картинка из статьи для привлечения внимания


JavaScript взрослеет


На заре Веб взаимодействие с браузерами было таким: веб-браузер запрашивал каждую страницу (например, www.geocities.com) заставляя сервер, находящийся где-то в Интернете генерировать HTML, и отсылать обратно. Этот был хороший подход, так как браузеры в то время были еще не особенно мощными, да и HTML-страницы, в основном, были статичными и автономными. JavaScript, созданный чтобы позволить веб-страницам быть более динамичными, не позволял сделать что-либо более сложное чем слайд-шоу или виджет подбора даты.

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

Одностраничные приложения


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

О библиотеках типа backbone.js, ember.js и angular.js часто говорят как о клиентских MVC или MVVM библиотеках. Классическая клиентская MVC архитектура выглядит примерно так:

image

Большое количество логики приложения (представления, шаблоны, контроллеры, модели, интернационализация и т.д.) живет на клиенте, для данных используется специальное API. Сервер может быть написан на любом языке, таком как Ruby, Python или Java. В большинстве случаев их роль заключается в отдаче каркасной страницы. Как только JavaScript файлы загружаются браузером, они выполняться, клиентское приложение инициализируется, подгружаются данные через соответствующее API и отрисовывается оставшаяся часть HTML.

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

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

Неприятности в Раю


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

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

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

Производительность

По той же самой причине, если сервер не генерирует страницу целиком, а вместо этого приходится ждать загрузки JavaScript на клиент, пользователи несколько критических секунд видят пустую страницу или значок загрузки. Проводилось множество исследований изучающих то, какой эффект оказывают медленные сайты на пользователей, и, соответственно, на прибыль. Amazon утверждает, что каждое 100 мс сокращение загрузки увеличивает прибыль на 1%. Twitter потратил год совместных усилий 40 разработчиков переделывая сайт, чтобы генерировать страницы целиком на сервере вместо клиента, получив 5-кратное улучшение воспринимаемого времени загрузки.

Поддержка

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

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

Гибридный подход


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

Чтобы добиться этого, мы, в Airbnb, экспериментируем с «изоморфными JavaScript приложениями», JavaScript приложениями, которые могут работать как на клиенте так и на сервере.

Изоморфное приложение выглядит примерно так. Озаглавлено “Клиент-серверное MVC”:

image

Здесь часть логики приложения или отображения может выполняться как на сервере так и на клиенте. Это дает много возможностей: оптимизация выполнения, лучшая поддержка, СЕО по умолчанию, и более контролируемые веб-приложения.

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

Изоморфный JavaScript в дикой среде


Идея не нова — Nodejitsu сделал хорошее описание архитектуры изоморфного JavaScript еще в 2011 г. — но она была трудно адаптируемой. С тех пор появились еще несколько изоморфных фреймворков.

Mojito был первым известным изоморфным фремворком с открытыми исходными кодами. Это продвинутый, фулстек-фреймворк, основанный на node.js, но он зависел от yui, да и причуды yahoo не способствовали его особой популярности в JavaScript сообществе с момента, как он стал открытым в апреле 2012 г.

Meteor — вероятно самый широко-известный изоморфный проект сегодня. Метеор создан с нуля для поддержки приложений реального времени, и его команда создала целую экосистему вокруг своей пакетной системы и инструментов развертывания. Как и Mojito, это большой, чрезмерно самоуверенный node.js-фреймворк. Однако многое было сделано для привлечения JavaScript-сообщества, и их наиболее ожидаемый релиз 1.0 уже не за горами (прим. пер. в начале 2014 г.). Метеор — это проект за которым нужно следить — звездная команда, $11.2 млн. финансирование от Andreessen Horowitz — неслыханно для компании, полностью сфокусированной на выпуске продукта с открытыми исходными кодами.

Asana, приложение по управлению задачами основанное Facebook с со-основателем Дастином Московицем, имеет интересную изоморфную историю. Не испытывая проблем с финансированием, учитывая статус Московица, как самого молодого миллиардера в мире, Асана провела годы исследований, разрабатывая свой фреймворк Luna с закрытыми исходными кодами — один из самых продвинутых примеров изоморфного JavaScript. Luna, изначально созданная на v8cgi (в дни предшествующие node.js) позволяла запускать копию приложения на сервере для каждой пользовательской сессии. Для каждого пользователя запускался отдельный серверный процесс, выполняя тот же JavaScript-код который запущен на клиенте. Это давало все преимущества продвинутой оптимизации, такие как устойчивость к оффлайну и быструю реакцию.

Мы тоже создали свою изоморфную библиотеку в этом году. Называется она Rendr. Она позволяет создавать backbone.js + handlebars.js одностраничные приложения, которые так же могут полностью формировать страницы на сервере. Rendr — это результат нашего опыта переделки мобильной версии Airbnb для улучшения скорости загрузки, что особо важно для мобильных пользователей которым свойственны высокие задержки при передаче данных. Rendr — скорее библиотека, чем фреймворк, решающий несколько задач (если вы будете сравнивать с Mojito или Meteor), но он легок для изменения и расширения.

Абстракции, абстракции, абстракции


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

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

Нам нужен единый набор маршрутов, которые сопоставляют определенные URI-шаблоны соответствующим обработчикам. Этим обработчикам должны быть доступны: HTTP заголовки, куки, информация из URI и возможность перенаправления без прямого доступа к windows.location (в браузере) и req и res (в node.js).

Загрузка и сохранение данных

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

Генерация страницы

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

Сборка и упаковка

Оказывается написание изоморфного кода приложений — это только половина дела. Инструменты, такие как grunt и browserify — неотъемлемая часть процесса создания и запуска приложений. Для сборки обычно нужно выполнить несколько шагов: компиляция шаблонов, включение клиентских зависимостей, применение трансформаций, минификация и т.д. В простейшем случае нужно собрать весь код приложения, представление и шаблоны в единый пакет, но для больших приложений, это может обернуться необходимостью передавать сотни килобайт клиенту. Более продвинутый подход — это создать динамическую связку и предоставить возможность отложенной загрузки, однако в этом случае сложность быстро возрастает. Инструменты статистического анализа кода, такие как Esprima позволяют честолюбивым разработчикам провести продвинутую оптимизацию и метапрограммирование, чтобы уменьшить количество шаблонного кода.

Собираем из маленьких модулей


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

Очевидно, что большинство JavaScript-модулей могут уже быть использованы изоморфно либо без, либо с небольшими модификациями. Например, такие популярные библиотеки, как undrscore, backbone, handlebars, moment и даже jquery уже можно использовать на сервере.

Чтобы продемонстрировать все это, я создал небольшое приложение, названное изоморфный туториал которое вы можете взять с GitHub. Комбинируя вместе несколько модулей, каждое из которых может быть использовано изоморфно, довольно просто создать небольшое изоморфное приложения в сотню строк кода. Здесь используются: Director для серверной и браузерной маршрутизации, Superagent для HTTP запросов и handlebars для шаблонизации, все это собрано поверх обычного express.js приложения. Конечно с ростом сложности приложения, нужно будет добавлять дополнительные слои абстракции, но я надеюсь на то, что чем больше разработчиков будут экспериментировать с этим подходом, тем больше новых библиотек и стандартов будет появляться.

Оглядимся


Чем больше организаций будут запускать node.js в продакшен, тем неумолимей это будет приводить к тому, что все больше и больше кода будут разделяться между клиентом и сервером. Важно помнить, что изоморфный JavaScript можно использоваться и локально для узких и для широких задач — можно начать с разделения шаблонов, продвигаясь дальше до разделения всей подсистемы отображения, и завершая полным разделением бизнес логики приложения. Конкретный способ разделения JavaScript между средами целиком зависит от того как приложение создается и от его особенностей.

Nicholas C. Zakas отлично описал то, как по его мнению в приложениях произойдет переход UI-слоя с клиента на сервер, давая выигрыш в производительности и лучшую поддержку. Чтобы использовать изоморфный JavaScript, не нужно будет отказываться от существующего бэкэнда и заменять его node.js, выплескивая ребенка вместе с водой. Вместо этого, создавая правильный API и используя RESTful-подход, традиционный бэкенд может спокойно жить рядом с node.js.

В Airbnb мы уже начали переделывать процесс создания клиентской стороны, используя базирующиеся на node инструменты, такие как grunt и browserify. Наше основное rails-приложение может быть никогда и не будет полностью вытеснено node.js, но, используя их вместе, становится даже проще разделять определенные части JavaScript между окружениями.
Tags:изоморфный JavaScript
Hubs: Website development JavaScript
+32
75.9k 350
Comments 75