Upload
codefest
View
651
Download
4
Embed Size (px)
Citation preview
Изоморфный JavaScript
Будущее уже здесь
Денис Речкунов, Flamp
«Мартин, в будущем
JavaScript изоморфный!»
Что такое изоморфный JavaScript код?• Исполняется в различных окружениях
(нам важен сервер и браузер)
• Гарантирует одно поведение
• Справляется с разностью окружений через абстракции
3
Что он нам даёт?• Мы можем создать изоморфный Web UI
• Который будет строить HTML на сервере для SEO
• Работать как Single Page Application в браузере
• Получим единую языковую среду — JavaScript
• Максимально переиспользуем код
• Получим прирост производительности на старте (ms vs sec)
4
Что он нам не даёт?• Не работаем с хранилищем
• Как правило, работаем с RESTful API
• Не включает код для обеспечения безопасности
• Чаще всего это только Web UI
5
Как мы к этому пришли?
27 мая 2009 года вышел релиз
Автор — Райан Дал, разработчик из Joyent
Вот оно!
В 2011 году вышел релиз сборщика
Автор — Джеймс Холлидей (substack)
Словно Делориан для изоморфногоJavaScript
Люди начали его использовать
В своих проектах
18 октября 2011 появился пост Nodejitsu
"Scaling Isomorphic Javascript Code"
9 ноября 2011 nodejitsu представляетфреймворк
16 июня 2012 года Yahoo релизитфреймворк Mojito
Но термин "изоморфный"
Стал популярным благодаряСпайку Брехему из Airbnb
11 ноября 2013 появляется пост
"Isomorphic JavaScript:The Future of Web Apps"
Позже Спайк Брехм выступает наJSConf 2014 с докладом
"Building Isomorphic Apps"
Как добиться изоморфизма?
Нужно решить ряд проблем• Разный рендеринг
• Зависимость от окружения
• Запросы за данными к RESTful API по-разному
• Собирать серверный код для браузера
19
Разный рендеринг
Virtual DOM (на сервере)• Имитация DOM для фронт-енд фреймворка на сервере
• Приложение применяет изменения к эталонному DOM
• Происходит сериализация в строку HTML
• HTML уходит в браузер
21
Virtual DOM (в браузере)• Приложение делает изменения в Virtual DOM
• Состояние Virtual DOM сравнивается с DOM
• При найденном отличии применяется поэлементный patch к DOM
• Используется в React
22
Мартин не в восторге от Virtual DOM
Template Helpers (на сервере)• Берем шаблонизатор, например Handlebars
• Определяем Helper, который будет рендерить View с данными
• При вызове хелпера запрашиваем данные для шаблона
• Рендерим шаблон с данными, создавая элемент-обертку с меткой
• Когда отрендерили все View – отдаем HTML браузеру
24
Template Helpers (в браузере)• Компилируем шаблоны для браузера
• Клиентский код привязывается к помеченным элементам-обёрткам
• Обновляем поддеревья DOM (каждую View полностью)
25
Мартин по-прежнему не сильно рад
Прогрессивный рендеринг (на сервере)• Используем кастомные тэги HTML
• Используем Node.js Streams API и реализуем Transform Stream
• ourTransform.pipe(response).end(root.render());
• Transform Stream ищет наши тэги в потоке HTML
• Если нашел—рендерит в них соответствующий шаблон
• Который пропускается через такой же Transform Stream
• Весь отрендеренный HTML сразу порциями уходит в браузер
27
Когда у вас прогрессивный рендеринг
29
Прогрессивный рендеринг (в браузере)• Компилируем шаблоны для браузера
• Используем привязки клиентского кода по нашим тэгам HTML
• Обновляем поддеревья DOM (каждый HTML-тэг полностью)
• Используется в Catberry.js
30
Мы с Мартином выбираем этот вариант
Как балансировать между серверным ибраузерным окружением
Серверный роутерRouter.prototype.route = function (request, response) {
var context = { ... };
var state = this.getState(request.url);
renderer.render(state, context, response);
};
01.
02.
03.
04.
05.
33
Серверный контекстvar context = {
userAgent: request.headers['user-agent'],
location: request.url,
redirect: function (url) {
response.writeHead(302, {Location: url});
},
getCookie: function () { return request.headers.cookie; }
setCookie: function (string) {
response.setHeader('Set-Cookie', string);
}
};
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
34
Браузерный роутерRouter.prototype.route = function (state) {
var context = { ... }
renderer.render(state, context);
};
01.
02.
03.
04.
35
Браузерный контекстvar context = {
userAgent: window.navigator.userAgent,
location: window.location.pathname + window.location.search,
redirect: function (url) {
window.location = url;
},
getCookie: function () { return document.cookie; }
setCookie: function (string) {
document.cookie = string;
}
};
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
11.
36
На что срабатывает роутинг?window.addEventListener('popstate', function (event) {
router.route(event.state);
});
window.document.body.addEventListener('click', function (event) {
event.preventDefault();
var location = window.location.toString();
var state = router.getState(location);
window.history.pushState(state, '', location);
router.route(state);
});
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
37
Ну а остальное детали
Запросы к API по-разному• На сервере http или https модули node.js
• В браузере XMLHttpRequest
• Есть готовые решения
(superagent, catberry-uhr, iso-http)
39
Собирать серверный код для браузера• Для нас это уже делает browserify
• Умеет подставлять браузерные версии модулей node.js
• Делает заглушки для того, что работать не может
• Можно указать в package.json замену на браузерную версию
40
package.json"browser": {
"./lib/Renderer.js": "./browser/Renderer.js",
"./lib/Router.js": "./browser/Router.js"
}
01.
02.
03.
04.
41
И так, что мы имеем?• Рендеринг можно реализовать несколькими способами
• С разницей окружений вполне можно разобраться
• Есть готовые изоморфные npm-пакеты для HTTP-запросов
• Browserify решает проблемы со сборкой
42
Готовые фреймворки:• Meteor
• Derby
• React
• Catberry.js
• Slot
• Taunus
• ещё на isomorphic.net
43
Будущее уже здесьНесколько изоморфных веб-приложений:
• instagram.com
• flickr.com
• airbnb.ru
• 2gis.ru
• konfettin.ru
• flamp.ru
44