Предварительное исследование GraphQL и Relay

задняя часть внешний интерфейс GraphQL React.js

Прошел уже почти год, как Facebook выпустил новую версию Relay (Relay Modern), но актуальных китайских материалов и практических кейсов до сих пор не так много. Причина может быть связана с отсутствием подробностей в официальной документации. Благодаря анализу GraphQL и Relay в этой статье мы надеемся уменьшить сложность начала работы, и в то же время легко судить, подходит ли ваш бизнес для использования Relay framework.
Какой? код напрямую? Monkey ~ можно клонировать на GithubШаблон приложения ретрансляции, который объединяет интерфейс и серверную часть, а также маршрутизацию, что в основном может удовлетворить потребности обычных приложений.

GraphQL

GraphQL — это независимая система запросов данных, о ее внедрении и использовании см.Официальный сайтСделано более подробное введение, и в то же время теперьКитайская версияможно обратиться. Основные понятия рекомендуется читать непосредственно на официальном сайте, эта статья не содержит подробностей.

дизайн-мышление

Причина, по которой Graph включена в название, заключается в том, что GraphQL использует метод запроса структуры графа. Возьмите пример:
Мы хотим спроектировать внутреннюю систему управления персоналом компании, предполагая простейший сценарий, который будет содержать как минимум две основные информации об отделах и сотрудниках. Чтобы представить их отношения в структуре графа, это может выглядеть так:

Оглядываясь назад на обычно используемые системы, можно обнаружить, что ролевые отношения в основном могут быть описаны графами. Здесь мы можем сравнитьграф базы данныхКонцепция чего-либо. Поскольку структура графа ближе к естественному миру по сравнению с реляционной базой данных, при проектировании базы данных графа будет сохранено преобразование из структуры графа в реляционную структуру. Дополнительные сведения о базах данных графов см.Введение NEO4J.
Возвращаясь к приведенному выше рисунку, предположим, что теперь мы хотим запросить подробную информацию о сотруднике L. Используя GraphQL, мы можем запросить ее следующим образом (для большей наглядности здесь это выражено на китайском языке):

{
  员工(ID: "022") {
    姓名
    职位
    所属部门 {
      名称
    }
    同事 {
      ID
      姓名
      职位
    }
  }
}

Обычно сервер возвращает следующий результат:

{
  员工 {
    姓名: "L某",
    职位: "员工",
    所属部门:{
      名称: "前端部"
    }
    同事:[
      {
        ID: "022",
        姓名: "S某",
        职位: "经理"
      },
      {
        ID: "033",
        姓名: "Y某",
        职位: "总监"
      }
    ]
  }
}

Видно, что по запросу запроса вся информация о сотрудниках возвращается в формате Json. В соответствии с рисунком эта часть информации, выделенная синим цветом, фактически извлекается:

Запрос берет сотрудника (L) в качестве отправной точки и извлекает необходимые связанные данные по краю, чтобы сформировать окончательный возвращаемый результат. Из этого видно, что GraphQL на самом делеДанные структуры графа извлекаются в древовидную структуру. Чтобы отразить это более четко, мы вынесем синюю часть отдельно и немного изменим положение узла:
Здесь может возникнуть вопрос: Итак, проверьте древовидную структуру, есть неизбежные требования к ребрам между задействованными узлами.Что делать, если я хочу запросить информацию о двух узлах одновременно, но между ними нет границы?
Продолжая предыдущий пример, если я хочу запросить информацию о сотруднике L и отделе дизайна, но между ними нет границы. В это время вы можете создать виртуальный узел и использовать его в качестве отправной точки для подключения других узлов, которые необходимо опрашивать. Вероятно, это будет такая структура:
Структура запроса:

{
  员工(ID: "022") {
    姓名
    职位
    ...
  }
  部门(名称: "设计部") {
    名称
  }
}

Возвращаемый результат:

{
  员工 {
    姓名: "L某",
    职位: "员工",
    ...
  }
  部门 {
    名称: "设计部"
  }
}

При реальной разработке приложений обычно используется описанная выше структура проекта.
Вот, подведем итоги. GraphQL извлекает данные структуры графа в древовидную структуру с помощью оператора запроса и возвращает его в качестве результата. Данные структуры графа здесь соответствуют GraphQLсистема типов. Как связать узлы в графе, то есть определенные типы, необходимо реализовать с помощью определенных логических кодов. Официальный сайт и сообщество также доступны на разных языках.Библиотека GraphQL.
Тогда есть еще вопрос,Что делать, если я хочу изменить данные?
Чтобы решить эту проблему, GraphQL вводит понятие мутации. Мы можем думать о мутации как о специальном запросе, вам нужно определить имя, параметры, вернуть данные для него и выполнить его конкретные операции с данными в определенной логике кода. Для получения подробной информации см.официальная документация.

Преимущество

Официальный сайт кратко представляет некоторые преимущества GraphQL, однако вам может быть интересно, каковы преимущества GraphQL по сравнению с другими шаблонами проектирования API?
Согласно моему практическому пониманию, GraphQL в основном решает некоторые противоречия, которые часто возникают при разработке и последующем обслуживании интерфейсных API. Давайте подробно рассмотрим RESTful API ниже.

гибкость

Давайте рассмотрим пример из предыдущего раздела. Такие сценарии, как получение всей информации о сотрудниках, относительно распространены при разработке приложений. Что, если их заменить RESTful API? Я думаю, что обычно есть два подхода:

  • 1. Отдельный API для извлечения всей информации;
  • 2. Используйте информацию о «коллегах» и «отделах» как независимые API и объединяйте их через несколько API во внешнем интерфейсе.

Сначала рассмотрим первый подход. Этот подход имеет несколько очевидных недостатков:
Во-первых, это избыточность информации. Если мне нужно отображать только базовую информацию о сотрудниках в другом месте, без информации о конкретном отделе или коллеге, возникает избыточность данных.
Во-вторых, с точки зрения серверной части, это потребует затрат на обслуживание. Из-за относительно изменчивых требований к отображению внешнего интерфейса это, вероятно, приведет к тому, что многие API-интерфейсы больше не будут использоваться, и эти API часто бывает нелегко удалить. Или, в новых требованиях, вероятно, будут добавлены API, которые часто повторяются с существующими API, что приводит к избыточности внутреннего бизнес-кода.
Кроме того, даже принимая во внимание метод передачи параметров, возвращаемое значение можно сделать необязательным, что действительно может повысить степень гибкости, но неизбежно увеличивает сложность параметров.
Посмотрите на второй подход. Благодаря этому методу мелкозернистого деления модулей, по сравнению с первым, стоимость обслуживания внутреннего кода снижается, но он крайне недружелюбен к внешнему интерфейсу. Например, для поля в списке серверная часть предоставляет только отдельный метод запроса.Когда объем данных в списке особенно велик, количество запросов также сильно увеличивается, что напрямую влияет на производительность интерфейса и пользователя. опыт. Кроме того, большое количество асинхронных запросов, несомненно, усложняет интерфейсный код, тем самым увеличивая стоимость обслуживания фронтенда.
GraphQL необходимо только определить тип и соответствующий способ обработки данных и разоблачить его к коренному узлу запроса. Передний конец может запросить по мере необходимости, что очень хорошо решает вышеуказанные противоречия.

Удобство внешнего интерфейса

GraphQL как слой BFF (Backend For Frontend) имеет свои неотъемлемые преимущества, наиболее важным из которых является дружественный интерфейс.
В дополнение к упомянутому выше, в запросе запроса, в дополнение к сокращению количества запросов для улучшения внешнего интерфейса, Мутация также уменьшает количество запросов. В Restful API, в дополнение к GET-запросу, после выполнения других запросов интерфейсу обычно необходимо выдать еще один GET-запрос, чтобы получить измененные данные. Если в возвращаемый результат будут добавлены некоторые данные для внешнего интерфейса отображения, это разрушит возможность повторного использования внутреннего кода. В мутации GraphQL возвращаемые данные могут быть полностью определены в соответствии с требованиями к данным внешнего интерфейса, и требуется только один запрос. В сочетании с платформой Relay вы также можете определитьидеализированное обновлениеЧтобы уменьшить количество появлений интерфейса загрузки и улучшить взаимодействие с пользователем. (Это будет расширено в следующем разделе)

Сокращение расходов на связь

В современной популярной модели разработки с разделением клиентской и серверной части затраты на связь разработчиков клиентской и серверной частей также являются основным фактором, влияющим на ход проекта. Обычно, чтобы снизить затраты на связь, бэкенд-разработчикам необходимо заранее определить документы API, а фронтенд будет MOCK данных в соответствии с документами API для разработки интерфейсов. Backend-разработчикам также необходимо использовать различные инструменты, такие как PostMan для тестирования. После того, как front-end и back-end разработка завершены, необходимо провести совместную отладку и стыковку.
Для GraphQL,schemaХорошая документация сама по себе. В то же время официальный также предоставляет инструмент, аналогичный PostMan.GraphiQL, что может помочь при отладке в процессе разработки.

разное

После объединения GraphQL с платформой Relay он также может давать большие преимущества, такие как непротиворечивая проверка типов на передней и задней частях, внешнее кэширование и т. д. Подробности см. в следующем разделе.

Relay

Relay — это фреймворк, основанный на GraphQL и React, который объединяет их на основе исходных компонентов React, а такжепроситьРасфасован по компонентам.
Официально предоставляет TodoMVCdemoДля справки, он в основном охватывает операции CRUD.

QueryRenderer

Фреймворк Relay обеспечиваетQueryRendererТакой компонент более высокого порядка (HOC) для инкапсуляции компонентов React и запросов GraphQL. Этот компонент принимает четыре реквизита:environment, запрос, переменные и визуализация. среда должна быть настроенаСетевые запросыа такжеStore; query принимает запрос GraphQL; variable принимает переменные, необходимые в запросе GraphQL, и, наконец, render используется для определения рендеринга компонента.
Предположим, мы хотим разработать компонент Relay, отображающий основную информацию о сотрудниках, тогда он может выглядеть так:

<QueryRenderer 
  environment={environment}
  query={graphql`
    query StaffQuery($id: ID!) {
      员工(ID: $id) {
        ID
        姓名
        职位
      }
    }
  `}
  variables={{ id: '011' }}
  render={({error, props}) => {
    if (error) {
      return <div>{error.message}</div>;
    } else if (props) {
      return <div>工号:{props["员工"]["ID"]};姓名:{props["员工"]["姓名"]};职位:{props["员工"]["职位"]};</div>;
    }
      return <div>Loading...</div>;
    }
  }
/>

Fragment

Теперь, когда у нас есть компонент, который отображает основную информацию о сотрудниках, что, если мы хотим дополнительно инкапсулировать компонент списка сотрудников на основе этого компонента?
Ссылаясь на компонент React, вы можете создать новый компонент, который получает реквизиты, содержащие массив идентификаторов сотрудников.В этом новом компоненте сопоставьте информацию о нескольких сотрудниках на основе массива идентификаторов.Релейные компоненты.
Вроде работает, но проблема в том, что если ID 10, то такой компонент тоже будет делать 10 запросов GraphQL, что явно идет вразрез с философией дизайна GraphQL.
Конечно, вы также можете создать новый компонент Relay: напрямую запрашивать набор данных о сотрудниках в запросе и отображать список. Но при этом теряется возможность повторного использования компонентов, поскольку очевидно, что в этом новом компоненте логика и стиль отображения информации о каждом сотруднике согласуются с компонентом информации об одном сотруднике.
Здесь Relay обеспечиваетFragmentКомпонент HOC, который принимает два реквизита: component и fragmentSpec.
компонент принимает компоненты React для обработки конкретных представлений компонентов и логики; fragmentSpec принимает разделGraphQL Fragment. Так называемый Фрагмент, соответствующий графу в предыдущем разделе, представляет собой определенную часть узла. Например:

fragment 员工信息 on 员工 {
  ID
  姓名
  职位
}

В запрос можно ввести Fragment так:

{
  员工(ID: "022") {
    ...员工信息
  }
}

Затем вернитесь к Relay, вы можете создать фрагментный компонент информации о сотрудниках следующим образом:

createFragmentContainer(
  class 员工信息 extends React.Component {
    render() {
      return <div>工号:{this.props.data["员工"]["ID"]};姓名:{this.props.data["员工"]["姓名"]};职位:{this.props.data["员工"]["职位"]};</div>;
    }
  },
  graphql`
    fragment 员工信息 on 员工 {
      ID
      姓名
      职位
    }
  `,
)

С таким компонентом Fragment для информации о сотрудниках мы можем создать компонент для списка информации о сотрудниках:

<QueryRenderer 
  environment={environment}
  query={graphql`
    query StaffListQuery($ids: [ID]!) {
      员工(IDs: $ids) {
        ...员工信息
      }
    }
  `}
  variables={{ id: ['011', '022', '033'] }}
  render={({error, props}) => {
    if (error) {
      return <div>{error.message}</div>;
    } else if (props) {
      return <员工信息 data={this.props.data} />;
    }
      return <div>Loading...</div>;
    }
  }
/>

Таким образом, фактический запрос компонента по-прежнему только один, но компонент информации о сотрудниках был успешно повторно использован.Если вам нужно отобразить информацию о сотрудниках в других компонентах, вам нужно только импортировать компонент Фрагмент.
В дополнение к базовому контейнеру фрагментов Relay также предоставляетRefetch Containerа такжеPagination Containerкомпонент, первый вводится на основе исходного компонента Fragmentrefetchметод, чтобы соответствовать сценариям, когда компоненту необходимо обновить данные (например: пользователь активно нажимает кнопку обновления списка данных); в последнем добавлено несколько операций подкачки, которые здесь не будут раскрываться.

Relay Store

В среде, настроенной в QueryRenderer, он в основном содержит сетевые запросы и Store. Магазин здесь находится по адресуReduxМагазин не совсем соответствует. Redux в основном используется для унифицированного управления состоянием компонентов, в то время как Relay Store записывает Record. Запись здесь фактически является каждым типом GraphQL или соответствует каждому узлу на графике в предыдущем разделе.
Когда платформа Relay получает данные, возвращенные GraphQL, она записывает идентификатор для данных каждого узла и сохраняет его как запись в хранилище Relay. В то же время Relay также предоставляет методы CRUD для этих записей. Для получения подробной информации см.официальная документация.

Mutations

Чтобы облегчить инициирование операций GraphQL Mutation в компонентах, Relay предоставляетcommitMutationметод. В дополнение к запуску мутации, с помощью релейного хранилища можно легко находить и обновлять данные страницы, а также достигатьидеальное обновление, чтобы еще больше улучшить взаимодействие с пользователем.

Преимущество

До сих пор были рассмотрены в основном все функции Relay. Как видно из вышеизложенного, исходя из первоначальных преимуществ GraphQL, Relay также имеет следующие два преимущества:

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

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

Суммировать

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

использованная литература

GraphQL Concepts Visualized
GraphQL и анализ реле