js lab`16. Максим Климишин: "smarter react.js: ui faster, ux better"
TRANSCRIPT
Обо мне
‣ 12+ лет опыта веб разработки, 6 лет JavaScript, 7 лет Python, год с Clojure
‣ Работал в oDesk (Upwork), Helios, 42CoffeeCups.
‣ Со-организатор конференций PyCon Ukraine, KyivJS, KyivPy, Hotcode
‣ с 2012 года работаю CTO в GVMachines Inc.
GVMachines Inc.
‣ Стартап по доставке продуктов питания из супермаркетов
‣ Работаем в Украине как ZAKAZ.UA (Киев, Днепропетровск, Харьков, скоро в вашем городе) и в США как CartFresh (Бостон)
‣ Python на бекенде
Сегодня поговорим о
Тема
‣ Что у нас было
‣ Почему мы остались с React.js
‣ Как сделать быстрее
‣ Планы
Архитектура
Как это было
Архитектура ПО – это фундаментальные структурные решения, которые будет
несоизмеримо дорого изменить после реализации
Что у нас было
Тема
‣ Python + Django
‣ Solr + специфичные индексы
‣ Redis
‣ jQuery + jQuery UI + bootstrap 2
Maintainability
Как это было
Поддерживаемость системы – это когда стоимость первоначальной реализации существенно больше в сравнении со
стоимостью изменений
Как это было
‣ Кода ставало больше, менять было сложнее.
‣ Корзина генерилась в iframe огромным куском Django-шаблона. Работать было крайне сложно.
‣ Страница продукта – сложный микс контекста для шаблона, шаблона и нескольких js файлов
‣ JS код в перемешку с Django Templates
‣ Фикс одной ошибки порождал две новые
Стратегия
Разработка
‣ Опробовать новые технологии: Angular, Backbone, React.js
‣ Внедрять постепенно, не ломая по дороге и не переписывая весь проект
‣ Интеграционные тесты
Результат
Разработка
‣ Angular отпал сразу как слишком сложный
‣ Backbone.js был, но на тот момент у него были проблемы с утечкой памяти и никакого преимущества по сравнению с jQuery кодом не было
Разработка
React.js прижился, посколько ощутимо выигрывал в производительности перед фреймворками, при этом не требовал слишком глубоко знания инструмента
Разработка
Ключевые преимущества‣ Если прегенерировать код на сервере – реакт не будет этого делать на клиенте
‣ Для генерации достаточно подать одинаковое состояние
‣ Если состояние между генерацией и отображением изменится – реакт произведет минимальное количество изменений DOM
РезультатРазработка
Короче, мы купились на то, как команда React.js превратили O(n3) проблему в O(n) с помощью двух простых допущений
Планируем
Процесс
‣ Переход плавный, для начала переписываем самую ключевую часть – корзину
‣ Решили обойтись без common.js и т.п. –не все члены команды понимали как это работает
Планируем
Процесс
‣ Серверный рендеринг – второй этап
‣ Перевод каталога – третий этап
‣ Перевод взаимодействия с пользователем – четвертый этап
Все как обычно
Процесс
‣ После корзины 2 года мы шли к каталогу
‣ Серверный рендеринг реализован несколько месяцев после полноценной работы каталога
Разница в перспективе
Процесс
‣ По ходу знакомство с новыми концепциями: CSP, Immutable DS, Clojure, CRDT, Haskell
‣ Теория очередей, практическая работа по моделированию
Процесс
‣ Вторая итерация затянулась с аутсорсом
‣ Привычка генерировать состояние server-side добавила геморроя
‣ Изменения вынудили написать новый бекенд для API
Плохие решения
Процесс
‣ Строить новую систему максимально используя старый бекенд привело к нереальному усложнению кода бекенда
‣ Использовать подход с $.ajax внутри React.js компонент привело к усложнению и copy-paste-style коду на клиенте
Плохие решения
Процесс
‣ Продолжить использование глобальных переменных
‣ Продолжить использовать глобальные события
Унифицированный API
Детали
‣ Решение вынести сложность по генерации состояния в API с возможностью композиции и аггрегации
‣ Один API для всех – Mobile app, website front-end
Унифицированный API
Детали
‣ Решение вынести сложность по генерации состояния в API с возможностью композиции и аггрегации
‣ API использует как бекенд так и клиент
‣ Избавило от необходимости отдавать сайт через node.js сервер
Side-effects
Детали
‣ Параллельные запросы в одном API вызове
‣ Простые и понятные ошибки валидации аргументов
‣ Генерация документации на базе простой интроспекции API вызовов
Пример запроса
Детали
{ "meta": {"session_id": “%SESSION_ID”}, "request": [ { "type": "timewindows.list", "args": { "store_ids": ["00002111"], "zone_id": "02111" }, "v": "0.1" } ] }
Пример доки
Детали
MODULE: timewindows-------------------------------------------RESOURCE: timewindows.list{'count': <Or(<Int(gt=0)>, <Null>)>, 'index': <class 'trafaret.Int'>, 'only_available': <class 'trafaret.Bool'>, 'store_ids': <List(<String>)>, 'zone_id': <String>}
Архитектура
API SERVERS
request state
generated state response
RENDERING WORKER
REACT.JS DOM RENDERER
put html into cache
CACHE SERVERS
time
Планы
‣ детерминированная модель для инвалидации сгенерированного дома дерева
‣ Уменьшение количества асинхронного кода в JS (CSP)
‣ Неизменяемые структуры (Redux, Immutable)
‣ Работа с корзиной через CRDT-базу
Планы
static state (state, channel, n=0) { // we could use CSP channels here return go(function * () { yield put(channel, title(“About"));
var talks = yield take(json({url: “/api/talks.json”}));
yield put(channel, [“talks", talks]);
channel.close() }) }
Communicating sequential processes