Прощай, Redux
Полный разбор того, почему нам был нужен Redux в прошлом, и почему он не нужен в будущем.
Перевод статьи Jack Scott: Goodbye Redux. Опубликовано с разрешения автора.
В течение последних нескольких лет интернет-технологии смещаются в сторону фронтенд JavaScript-фреймворков с целью улучшения пользовательского опыта в вебе и мобильных приложениях. Это потрясающе 🔥 Мне лично нравится гибкость, которую нам предоставляют фреймворки.
Но масштабы этой гибкости зашли слишком далеко…
Чтобы действительно понять, почему это так, давайте перемотаем время вспять и посмотрим, как приложения создавались до появления JavaScript-фреймворков.
⏳ Земля до начала JavaScript…
До разработки первых фронтенд фреймворков, наиболее заметными из которых являются AngularJS, Backbone и Ember, мы отрисовывали шаблоны на сервере, а затем отправляем полную HTML-страницу в браузер. Популярными фреймворками в то время были:
- Django (Python) — Первый релиз 21 июля 2005; ~13 лет назад.
- Ruby on Rails (Ruby) — Первый релиз 13 декабря 2005; ~13 лет назад.
- Symfony (PHP) — Первый релиз 22 октября 2005; ~13 лет назад.
Эти фреймворки базировались на концепции MVC, структуре разработки приложений, которая расшифровывается как Model-View-Controller (Модель-Представление-Контроллер). Модели отвечали за «структуру» данных, представления являлись шаблонами, которые показывали, как отображать данные, а контроллеры соединяли модели с шаблонами.
Я имею в виду JavaScript, но на деле мы имели jQuery-слайдеры и другие странные библиотеки, которые использовались для создания совершенно ненужных прыгающих эффектов.
Приложения, созданные с помощью этих фреймворков, имели некоторые проблемы, но в целом все работало очень хорошо. Затем в один прекрасный день у Райана Дала появилась потрясающая идея начать использовать JavaScript, как нечто большее, чем просто инструмент для создания дурацких анимаций. Он разработал первую версию Node.js, которая позволила разработчикам писать JavaScript не только для браузера, но и для сервера.
- Node.js — Первый релиз 27 мая 2009; ~9 лет назад.
Внезапно люди увидели силу JavaScript и его способность выполнять большую работу с небольшим количеством кода. Это вдохновило разработчиков использовать возможности JavaScript. Люди не только начали создавать более эффективные инструменты для Node.js, но также начали разрабатывать интересные фреймворки для фронтенда. Это привело к эффекту снежного кома в развитии JavaScript в течение следующих нескольких лет:
- Express.js (бэкенд) — Первый релиз 16 ноября 2010; ~8 лет назад.
- Backbone.js (фронтенд) — Первый релиз 12 октября 2010; ~8 лет назад.
- AngularJS (фронтенд) — Первый релиз 20 октября 2010; ~8 лет назад.
- Ember.js (фронтенд) — Первый релиз 8 декабря 2011; ~7 лет назад.
Это начало серьезных изменений в методах разработки приложений. Структура MVC, которая ранее обрабатывалась исключительно сервером, разделилась на два сегмента — сервер, который обрабатывал MC (модели и контроллеры) и фронтенд-клиент, который обрабатывал представление, используя один из вышеперечисленных JavaScript-фреймворков. Некоторые из этих ранних фреймворков также включали в себя модели и контроллеры на уровне представления. Двойные модели и контроллеры: одни на клиенте, другие на сервере — теперь это звучит, как очень много избыточного кода! 🙇🏽
🤦 У Facebook была проблема
Все были очень счастливы. Все работало и было относительно легко в освоении, стоило только на пару часов погрузиться в изучение с головой.
Затем что-то произошло…
Стремительный рост Facebook привел к тому, что он стал крупнейшим веб-приложением в мире. Как вы можете себе представить, такое звание влекло за собой различные проблемы. Одной из основных причин для головной боли была задача показывать правильное количество уведомлений в панели заголовка.
Когда люди начали делать что-то в приложении Facebook, ожидалось, что эти уведомления будут обновляться соответствующим образом. Однако, это не всегда работало так. Если вы пользовались Facebook в то время, то вы наверняка помните, что эти уведомления всегда были неправильными… Проблема заключалась в том, что веб-приложениям трудно отловить изменения в одной части (например, когда вы читаете сообщение) и показать изменения в другой части (например, уменьшить количество непрочитанных сообщений на один).
Это не самая страшная проблема в мире — её можно решить, перезагрузив страницу. Но в Facebook было более 1000 непримиримых сотрудников, и они решили, что пришло время что-то с этим сделать. Поэтому они переосмыслили то, как фронтенд фреймворки обрабатывают информацию и решили создать свой собственный — React.
- React (фронтенд) — Первый релиз в марте 2013; ~5 лет назад.
Этот новый фреймворк был хорош для отрисовки HTML, но его идея также была очень банальна и не имела особого подхода к тому, «как» разрабатывать приложения. Таким образом, они также запустили Flux, который в конечном итоге превратился в то, что мы называем Redux (Redo-Flux). Ниже приведено видео, которое было на сайте Flux еще в 2014/2015 году. Видео пытается объяснить работу Flux и React.
🌪 …Затем все пошло наперекосяк
Принцип работы Redux заключается в том, что он хранит всю динамическую информацию приложения в одном JavaScript-объекте. Когда какая-либо часть приложения должна отобразить некоторые данные, она запрашивает информацию с сервера, обновляет один объект JavaScript и затем показывает эти данные пользователям. Сохраняя всю информацию в одном месте, приложение всегда показывало правильную информацию, независимо от того, в какой части приложения вы находитесь. Это решило проблему уведомлений на Facebook.
Таким образом появился новый фреймворк для создания приложений, а именно комбинация React и Redux. Facebook удалось решить свою проблему, и все жили долго и счастливо, верно?
✋ Не совсем.
Проблема в том, что люди (и я в том числе) начали использовать единственный объект для хранения всей их информации предоставляемой сервером. Конечно, этот способ хранения позволяет поддерживать всю информацию в актуальном состоянии, однако, у этого подхода также есть 3 основных недостатка:
- Для того, чтобы все работало так, как нужно, требуется много дополнительного кода, написание которого занимает все ваше время.
- Когда вы храните весь код в одном месте, вы рискуете также хранить «устаревшие данные», что означает, что у вас могут появляться нежелательные данные в приложении из предыдущих состояний.
- Кривая обучения для новых разработчиков зашкаливает.
Нам удалось превратить простую задачу по отображению данных пользователям из нескольких простых шаблонов, как это делалось в 2005 году с помощью MVC-фреймворков, в монолитные фронтенд-приложения с количеством кода на фронтенде в 10 раз большим, чем на бэкенде. Например, недавно я разрабатывал простое приложение и использовал WakaTime для измерения времени, которого было потрачено на разработку. Вот результаты:
- Фронтенд-репозиторий с React и Redux — 32 часа.
- Бэкенд-репозиторий с Express и Mongoose — 4 часа.
Вы серьезно?? 🤯 Я потратил в 8 раз больше времени на фронтенд, чем на бэкенд. Давайте найдем причину, по которой это требует столько дополнительного кода. Ниже приведен пример шагов, которые необходимо выполнить, чтобы добавить основной запрос на получение данных (например, всех пользователей) в мой фронтенд-проект.
🚧 Предупреждение: следующие шаги - супер-технические, поэтому не беспокойтесь, если вы ничего не поймете.
- Создайте компонент, для отображения списка пользователей (здесь нет проблем).
- Вызовите
fetch
для API. - Добавьте новое поле в состояние (state).
- Добавьте новое действие (action) для обновления состояния.
- Добавьте новый thunk-метод, который выполняет вызов fetch, а затем обновляет состояние, используя наши новые действия.
- Добавьте новую thunk-функцию в наш компонент используя
connect()
, которая обернет функцию dispatch. - Извлеките данные из Redux-состояния снова, используя
connect()
. - Определите thunk-функцию и извлеченные данные в props компоненты.
- Вызовите метод thunk в функции
componentDidMount()
. - И наконец, отрисуйте данные в DOM.
Святые угодники… 10 шагов… В старые добрые времена Ruby on Rails мне нужно было всего лишь подставить данные в мой HTML-шаблон и БУМ! И это приведет к тому же результату. Я понял, что что-то нужно менять.
☝️ Новый подход
Redux отлично справлялся с решением проблемы синхронизации вашего фронтенд-приложения, однако, как упоминалось ранее, он принес свои проблемы. Подумайте, сколько дополнительной функциональности дает нам Redux?
По сути, мы переписали весь наш интерфейс, чтобы решить несколько тривиальных проблем…
В любом случае, Facebook видимо осознал это, и начал работать над новой технологией под названием GraphQL, чтобы помочь решить эту проблему. GraphQL — это модное слово на данный момент, но я не уверен, что все понимают, почему эта технология так хороша.
GraphQL совсем не похож на Redux. Еще раз, Facebook выпустил удивительный продукт, но не смог донести в массы, почему он так важен. Поэтому я потратил последние несколько минут на создание некоторого контекста для вас.
GraphQL — это автомобиль, а Redux — лошадь.
Что? Почему это Redux — лошадь?
Причина, по которой я сравниваю их, как лошадь и машину, такова: вы не считаете лошадь похожей на машину. Одна из них животное с четырьмя ногами, а другая — машина с колесами. Тем не менее, они обе пытаются достичь той же конечной цели — доставить человека в пункт назначения. Машина едет по дорогам и использует топливо, тогда как лошадь — величественное животное, которое может прыгать через препятствия. Они имеют разные преимущества, но в целом, автомобиль доставит вас к месту назначения намного быстрее, чем лошадь.
Так что за GraphQL?
Официальная документация описывает GraphQL как «язык запросов для API». Довольно абстрактно, не правда ли? Говоря про язык запросов, они подразумевают то, что он один заменяет API с сотнями HTTP-эндпоинтов. Поскольку эта технология еще молода, документацию и вспомогательные инструменты по-прежнему трудновато понять. Кривая обучения стремится вверх. Вот пример для наглядности.
GraphQL заменяет эндпоинты, такие как:
- GET
/users/1234567890
- POST
/cars
- PUT
/example/endpoints
Пользовательскими запросами, которые вы создаете, только когда вам это нужно. Например:
{
user(id: "1234567890") {
name,
email
}
}
Вернет:
{
"user": {
"name": "Luke Skywalker",
"email": "luke@iamyourfather.com"
}
}
Но подождите… пользовательские запросы… Внедрение может занять годы.
На самом деле, это не так. Причина кроется в том, что запрашивая нужные данные, вам внезапно не нужно делать массу запросов, что означает, что вам не нужно писать много кода для обработки ответов сервера. В конечном итоге, вы сохраняете уйму времени за счет кода, который теперь вам не нужно писать.
🤷 Но как это может заменить Redux?
Еще один хороший вопрос, спасибо, что спросили. Проще говоря, не может. Однако, он стимулирует не хранить всю вашу информацию в одном объекте, как это делает Redux. Это связано с тем, что каждый запрос предназначен для получения данных только для одной части приложения — не для всего. Это было бы антипаттерном (да и просто нелогично) хранить информацию, относящуюся к одной маленькой части, в общем для всего приложения месте.
Используя GraphQL, вы больше не нуждаетесь в Redux и тем самым избавляетесь от большого количества ненужного кода.
Также стоит отметить, что Redux и GraphQL могут сосуществовать в одном приложении. Это полезно, так как вы можете плавно интегрировать GraphQL в свое приложение Redux, если вы уже внедрили его. Вот несколько материалов о том, как вы можете использовать их вместе:
Использование Redux непременно приведет к выбору: использовать его, чтобы решить несколько тривиальных задач, и получить вместе с ним накладные расходы, или отказаться от него и потратить время на что-то другое.
Хорошо, тогда что вы используете вместо него?
Еще не так давно Redux был хорош для решения проблем. Тем не менее, с момента его создания индустрия веб-разработок сделала экспоненциальный рывок вверх. И с этим рывком значительное развитие получила область веб-сокетов.
Веб-сокеты — это прямое соединения между сервером и клиентом, чтобы первый мог сообщать второму, когда он обновляется. И что вы думаете? GraphQL поддерживает веб-сокеты прямо из коробки в виде абстракций, называемых подписками. Поэтому мы можем использовать эти подписки для обновления частей нашего приложения, которые мы бы хотели синхронизировать.
Главное отличие заключается в том, что вместо обращений клиента с запросом на обновление (используя Redux), сервер сообщает клиенту, что данные должны быть обновлены. В итоге, это приводит к тому же результату. Вот несколько примеров того, как можно реализовать работу веб-сокетов или подписок непосредственно с Mongodb или Mongoose.
🚀 Нас ждет потрясающее будущее!
До недавнего времени GraphQL находился в разработке, но сейчас его можно смело и удобно использовать в боевых проектах. Я не буду лгать, его документация довольно сложна в понимании и требует хороших знаний JavaScript и того, как работают серверы. Однако, если это не про вас, то, по крайней мере, вы знаете к чему стремиться. Вот ссылка на популярный учебник.
Есть также уйма полезных библиотек, которые могут помочь вам постепенно интегрировать GraphQL в существующие продукты. Вам не стоит волноваться о том, что придется переводить все приложение сразу. Библиотеки позволяют конвертировать клиент-серверное взаимодействие в ваших приложениях по частям. Apollo — одна из компаний, которые в настоящее время активно поддерживают это.
Это все. Я надеюсь, что эта статья помогла пролить свет на некоторые более сложные концепции.
Если вам понравилась эта статья, пожалуйста, сделайте несколько хлопков на оригинальной статье, автору будет приятно. Также можете оставлять комментарии под оригинальной статьей, Джек обещал всем отвечать.
А если вам понравился перевод, можете оставить несколько хлопков и на нем 🤓
Слушайте наш подкаст в iTunes и SoundCloud, читайте нас на Medium, контрибьютьте на GitHub, общайтесь в группе Telegram, следите в Twitter и канале Telegram, рекомендуйте в VK и Facebook.