Заголовок: Если вам понравилась наша статья, не забудьте нажать и подписаться на специальный выпуск Ali Nanjing Technology~ Эта статья воспроизведена изСпециальный выпуск Alibaba Nanjing Technology — Знание, приветствуем Даниэля и Маверикс для публикации вакансий, таких как разработка клиентского/внутреннего интерфейса Alibaba Nanjing, подробности см.Alibaba Nanjing искренне приглашает внешних партнеров присоединиться~.
Когда я некоторое время назад чистил Твиттер, я увидел, что большой V упоминал Аполлон один за другим, предсказывая, что он будет расти в 2018 году. У меня была возможность использовать GraphQL, после бурного перелистывания документации Apollo я решил отказаться от знакомого Redux в новом фронтенд-проекте и полностью использовать Apollo для написания слоя данных. Теперь, спустя месяц, я должен выйти и прославить этого «Бога Солнца».
GraphQL
Перенесемся в 2018 год, и GraphQL больше не будет новым термином. После короткой волны дискуссий за 15 лет кажется, что мало что было услышано. Однако за последние несколько лет Github постепенно повзрослел, и Github также полностью внедрил новую версию API с GraphQL. Я не буду здесь обсуждать сам GraphQL, он упрощает сбор данных между интерфейсом и сервером.
Redux
Когда дело доходит до управления данными во внешнем интерфейсе, первое, что приходит на ум, это Redux. Я думаю, что многие люди прошли этапы от незнакомого до знакомого с Redux. Это должно быть примерно так:
- Начало: Архитектура Flux, разработанная Facebook, очень мощная, ее используют все, поэтому я тоже буду ее использовать.
- Полгода: управление данными стало понятнее, и наконец-то не нужно возиться с setState в компонентах
- Один год: я инженер CRUD, пишу шаблонный список, использование редукции для страниц форм действительно отбрасывает, сколько еще кода?
- Полтора года: смотрел redux-action, redux-promise, dva, зеркало… и настраивал наиболее подходящие мидлвары и плагины под бизнес-сценарии команды. Код стал более лаконичным
- Два года: все метания выброшены. Я немного устал, но я не могу уйти.
Почему вы устали? Потому что однонаправленный поток данных Flux уже не в новинку. Большую часть времени хранилище хранит данные, запрашиваемые у бэкэнда, для них не важно, как сделать диспетчеризацию и сокращение, но стоит подумать о том, как спроектировать хранилище.
Когда Redux отвечает потребностям бизнеса
Возьмем реальный сценарий непосредственно в качестве примера:
Это очень распространенный список комментариев, мы начинаем писать свой, как только получаем запрос<Comments />
Компоненты в парадигме Redux нам неизбежно приходится писать по такой логике:
- существует
Comments
изdidMount
внутри,dispatch
один для получения данныхaction
,в этотfetch action
отправить запрос в течение. Чтобы выполнить загрузку, мы, вероятно, отправим другое действие, чтобы уведомить Redux о том, что мы инициировали запрос. - Если запрос выполнен успешно, мы отправляем действие, которое успешно запрашивает данные, а затем обрабатываем и обновляем данные в редюсере.
- существует
Comments
Внутри мы получили данные из реквизита и официально начали рендеринг.
Большая часть нашей работы уходит на то, как получить данные. И с какими проблемами мы сталкиваемся? Посмотрите на некоторые требования, которые могут задать менеджеры по продукту.
- Пользователи, которые создают или изменяют комментарии, должны сразу увидеть обновление в списке;
Просто, просто повторно запросите весь интерфейс списка! В целом этого достаточно, но требовательные продукты могут потребовать от вас «оптимистичных» обновлений, чтобы улучшить работу. Все в порядке, добавляйтеreducer
то есть.
- При наведении курсора мыши на аватар пользователя должны появиться подробные данные о пользователе (личный профиль, контактная информация...).
Во-первых, подумаешь, а может старший брат бэкенда добавить все эти поля в данные интерфейса комментария за меня? Он тебе без раздумий отказал, и вынул общий интерфейс для настройки пользователя. Учитывая, что количество пользовательских данных не маленькое, а в комментариях большое количество таких же пользователей, разумно не ставить их в список. От всего сердца я просто нормализую все структуры данных во внешнем интерфейсе и использую идентификатор пользователя в качестве ключа для хранения данных в хеш-таблице. Всего за один день вы получили идеальное решение.
Столкнувшись с таким сценарием, мы написали слишком многоимперативВ коде мы шаг за шагом описываем, как получить данные комментариев, извлечь все идентификаторы пользователей после получения данных комментариев, запросить повторное получение всех данных пользователей после дедупликации и так далее. Нам также необходимо рассмотреть детали замедления нормализации, кэширования, оптимистичных обновлений и т. д. И это именно то, с чем редукс не может нам помочь. Так что мы будем инкапсулировать более мощные библиотеки и фреймворки на основе Redux, но реальная ориентация на сбор данных кажется не очень подходящей.
Декларативный и императивный
Так как же обстоят дела в мире Аполлона?
import { graphql } from 'react-apollo';
const CommentsQuery = gql`
query Comments() {
comments {
id
content
creator {
id
name
}
}
}
`;
export default graphql(CommentsQuery)(Comments);
мы использовалиgraphql
(Аналогично подключению в Redux) Привяжите запрос GraphQL к компоненту комментариев как к компоненту более высокого порядка, и все готово. Это так просто? Да, нам больше не нужно описывать, как отправить запрос в didMount и как обработать запрошенные данные. Вместо этого мы доверим все эти дела за нас Аполлону, который грамотно поможет нам отправлять запросы на получение данных, когда они нам нужны, а затем сопоставлять данные с пропсами аннотаций и отдавать их нам.
Мало того, будет гораздо удобнее, когда мы будем делать операции обновления. Например, отредактируйте комментарий. Мы определяем операцию мутации graphql:
// ...
const updateComment = gql`
mutation UpdateComment($id: Int!, $content: String!) {
UpdateComment(id: $id, content: $content) {
id
content
gmtModified
}
}
`;
class Comments extends React.Component {
// ...
onUpdateComment(id, content) {
this.props.updateComment(id, content);
}
// ...
}
export default graphql(updateComment)(graphql(CommentsQuery)(Comments));
Когда мы вызовем updateComment, вы волшебным образом обнаружите, что данные комментариев в списке автоматически обновляются. Это связано с тем, что apollo-client автоматически кэширует данные в кеше в соответствии с типом, и любые данные, возвращаемые узлом GraphQL, будут автоматически использоваться для обновления кеша.В мутации UpdateComment мы определяем его возвращаемое значение, тип Comment Newly изменить комментарий и указать поля, которые необходимо принять,content
а такжеgmtModified
. Таким образом, apollo-client автоматически обновит данные в кеше с помощью идентификатора и типа, тем самым перерисовав наш список.
Глядя на оставшиеся требования, нам нужно расширить информацию о пользователе, когда указатель мыши наводится на аватар пользователя. В соответствии с этим требованием нам нужно не только определить, какие данные нам нужны, но и позаботиться о том, «как» получить данные (отправить запрос при наведении на аватар). Аполлон также оказывает нам «императивную» поддержку.
class UserItem extends React.Component {
// ...
onHover() {
const { client, id } = this.props;
client.query({
query: UserQuery,
variables: { id }
}).then(data => {
this.setState({ fullUserInfo: data });
});
}
}
export default withApollo(UserItem);
К счастью, здесь нам еще не нужно думать о кэшировании самостоятельно. Благодаря глобальному кешу данных Apollo, после того, как мы запросили пользователя A, повторный запрос данных с тем же идентификатором напрямую попадет в кеш, а apollo-client напрямую обработает данные в кеше, не отправляя запрос. В это время возникает проблема, предполагая, что я просто хочу каждый раз повторно запрашивать?
client.query({
query: UserQuery,
variables: { id },
fetchPolicy: 'cache-and-network'
});
Apollo предоставляет нам множество стратегий для настройки логики кеша, например, по умолчаниюcache-first
(предпочитаю использовать кеш), здесьcache-and-network
(сначала используйте кеш и одновременно запрашивайте обновления), иcache-only
а такжеnetwork-only
.
Вот некоторые из вещей, которые меня восхищают в GraphQL и Apollo. Когда вы начинаете думать с точки зрения GraphQL, вас больше заботит то, какие данные нужны вашим бизнес-компонентам, чем то, как их получить шаг за шагом. Большинство оставшихся бизнес-сценариев могут быть решены автоматически с помощью внешнего вывода типов данных и кэширования. Конечно, пространство ограничено, и есть много элегантных мест, о которых слишком поздно упоминать, таких как пейджинг, непосредственное управление кешем для достижения оптимистичных обновлений, опросы запросов и подписки на данные. Если у нас будет возможность, мы можем продолжить обсуждение дальше.
REST и другое локальное состояние?
Глядя на это, вы можете подумать: «GraphQL — это круто, Apollo — тоже круто, но мой бэкэнд — это REST, и я в настоящее время не общаюсь с ними». На самом деле это не так.Apollo Link был представлен начиная с версии 2.0 Apollo Client.Теоретически мы можем получать данные из любого типа источника данных через GraphQL.
«Через GraphQL» означает, что мы можем использовать оператор запроса, написанный на GraphQL, для получения данных либо в оставшемся API, либо в состоянии клиента, чтобы клиент Apollo мог управлять всеми данными в приложении для нас, включая кэширование и объединение данных.
const MIXED_QUERY = gql`
query UserInfo() {
// graphql endpoint
currentUser {
id
name
}
// client state
browserInfo @client {
platform
}
// rest api
messages @rest(route: '/user/messages') @type(type: '[Message]') {
title
}
}
`;
В таком запросе Query мы используем директиву GraphQL для объединения данных из GraphQL, состояния покоя и состояния клиента и абстрагирования их вместе для обслуживания. Точно так же мы можем также инкапсулировать соответствующую реализацию мутации.
Хвостик
Вышеупомянутое, вероятно, является поверхностной практикой использования Apollo и GraphQL в то время. Хотя у меня нет глубокого опыта, я чувствую более элегантное решение, которое Thinking in GraphQL привносит во внешний интерфейс, и эффективность полного решения для внешнего слоя данных, такого как Apollo Client. Я считаю, что в 2018 году они обеспечат еще больший рост и могут даже заменить Redux в качестве общего решения для управления данными.
Сообщества, связанные с Аполлоном, также относительно активны.dev-blog.apollodata.comТакже я часто публикую статьи, представляющие большую справочную ценность, если вам интересно, вы можете ознакомиться с ними~