Фронтендер понимает GraphQL, достаточно прочитать это

GraphQL

image

GraphQLВ последние годы он упоминается все чаще и чаще, и упоминался во фронтенд-ветках нескольких технологических конференций, в которых я недавно участвовал. В таких вещах все еще есть какая-то загадка, что нелегко понять, что это такое, просто взглянув на название. Итак, собираемся узнатьGraphQLчто именно это такое.

Что такое GraphQL?

первый,GraphQLИз Facebook, если вы, как и я, вообще его не знаете и не знаете, что он делает, то вы, должно быть, слышали о другом, называемомStructured QLс вещами. ЧТО?По сути, это SQL.

  • гм, иSQLТакой же,GraphQLэто язык запросов (Query Language)
  • то же самое иSQLТо же самое,GraphQLтакже является набором норм, напримерMySQLдаSQLТот же набор реализаций,Apollo, Relay...СлишкомGraphQLРеализация спецификации
  • а такжеSQLразница в том,SQLИсточником данных GraphQL является база данных, а источником данных GraphQL могут быть различные REST API, различные сервисы/микросервисы или даже базы данных.

这里借Apollo官网的一张图来说明GraphQL所处的位置

Вот изображение с официального сайта Apollo, иллюстрирующее положение GraphQL в архитектуре интернет-приложений.

несколько моментов времени

  • Спецификация GraphQL была открыта в 2015 году.
  • Операция «Подписки» была добавлена ​​в спецификацию в 2017 году.

Так почему он называетсяGraphШерстяная ткань?

Graphсмысл картины, вGraphQLВ мире все представляет собой граф, а это значит, что вы можете смоделировать свою бизнес-модель в виде графа.

Графики — мощные инструменты для моделирования многих явлений реального мира, поскольку они напоминают наши естественные ментальные модели и словесные описания основных процессов.

Здесь задействована теория графов.Graph DatabaseТакие знания, если вам интересно, вы можете выучить определенную песню.

Таким образом, мы можем получить общее представление о GraphQL. Итак, давайте взглянем на GraphQL в целом.

Какую проблему решает для нас GraphQL?

Проще говоря, с точки зрения внешнего интерфейса во многих статьях упоминается, что для замены Restful API нужно быть немного более конкретным:

  • Настройка полей API, использование полей по мере необходимости
  • Агрегация API, получение всех данных одним запросом
  • Серверной части больше не нужно поддерживать номер версии интерфейса.
  • Полный механизм проверки типов, обеспечивающий более надежный интерфейс
  • . . .

Прежде чем узнать, как достигаются вышеуказанные преимущества, давайте проведем простое изучение GraphQL.

Интерфейсное обучение GraphQL

Как фронтенд-разработчику, только с точки зрения фронтенда, чаще всего нам нужно заботиться только о двух операциях:

  • запрос: В GraphQL это ключевое слово относится к типу схемы (что можно понимать как протокол), что означает, что то, что вы хотите выполнить, является действием запроса, то есть запросом в CRUD.

  • мутация: означает, что действие, которое вы хотите выполнить, это добавление, удаление и изменение

queryа такжеmutationв совокупности именуемыеschema. На самом деле есть еще одинsubscriptionsВ 2017 году его добавили в спецификацию (spec), что позволило нам проще реализовать функцию push.

Здесь мы берем два сценария внутренней платформы обмена в качестве примера, чтобы показать, как использовать эти две операции.

операция запроса

Прежде всего, в самом простом сценарии домашняя страница платформы обмена должна вызывать интерфейс для получения всех списков обмена Текущий метод вызова этого интерфейса:

GET /api/share/allShares

вернуть

{
    "shares": [
        {
            "shareId": 1238272,
            "title": "分享一下Vue3.0",
            "desc": "Vue3.0就要发布了,带来了哪些新功能呢?",
            "where": "6F-19会议室",
            "startTime": 1548842400
        },
        {
            "shareId": 1238272,
            "title": "用flutter写app页面是一种什么样的体验",
            "desc": "用跨平台框架flutter来写app页面的初体验", 
            "where": "6F-17会议室",
            "startTime": 1548842400
        },
        {
            "shareId": 1238272,
            "title": "Cordova原理",
            "desc": "一起来了解一下Cordova",
            "where": "6F-19会议室",
            "startTime": 1548842400
        }
    ]
}

Так что вместо GraphQL мы можем написать

query {
	shares {
		title
		desc
		where
		startTime
	}
}

Хм? Обнаружено, что поле отсутствует.Если мы хотим перейти на страницу сведений, нам нужно знать общий идентификатор и изменить его.

# 给一个查询起一个名字是一个好习惯
query findAllShares {
	shares {
		# 为id起了一个别名,叫shareId
		shareId: id
		title
		desc
		where
		startTime
	}
}

На этом базовая операция запроса завершена.

нумерация страниц

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

GET /api/share/allShares?star=0&limit=10

вернуть

{
    "allShares": {
        "totalCount": 3,
        "shares": [
            {
                "shareId": 1238272,
                "title": "分享一下Vue3.0",
                "desc": "Vue3.0就要发布了,带来了哪些新功能呢?",
                "where": "6F-19会议室",
                "startTime": 1548842400
            },
            {
                "shareId": 1238273,
                "title": "用flutter写app页面是一种什么样的体验",
                "desc": "用跨平台框架flutter来写app页面的初体验",
                "where": "6F-17会议室",
                "startTime": 1548842400
            },
            {
                "shareId": 1238274,
                "title": "Cordova原理",
                "desc": "一起来了解一下Cordova",
                "where": "6F-19会议室",
                "startTime": 1548842400
            }
        ]
    }
}

Давайте продолжим трансформировать способ GraphQL, способ пейджинга:

# 分页方式
query findAllShares($start: Int!, $limit: Int = 10) {
	allShares (start: $start, limit: $limit) {
		totalCount
		shares {
		    shareId: id
		    title
		    desc
		    where
	    	startTime
		}
	 }
}

GraphQL предоставляет полное решение для подкачки, вы можете обратиться кPagination

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

// 获取分享的详情
GET /api/share/:shareId
// 分享详情返回
{
    "shareDetail": {
        "shareId": 1238274,
        "title": "Cordova原理",
        "desc": "一起来了解一下Cordova",
        "where": "6F-19会议室",
        "startTime": 1548842400,
        "attchments": "",
        "creatorId": 321,
        "lastUpdateTime": 1548842400,
        "logoUrl": "",
        ...
    }
}
// 获取分享的评论列表
GET /api/share/comments/:shareId
// 分享评论列表返回
{
    "commentInfo": {
        "totalCount": 5,
        "comments": [
            {
                "id": 1,
                "content": "非常不错",
                "userId": 213,
                "commentTime": 1548842400,
            },
            {
                "id": 2,
                "content": "很好",
                "userId": 214,
                "commentTime": 1548842400,
            },
            {
                "id": 3,
                "content": "不错",
                "userId": 216,
                "commentTime": 1548842400,
            },
            {
                "id": 4,
                "content": "Very GOOD!",
                "userId": 2313,
                "commentTime": 1548842400,
            }
        ]
    }
}
// 分享的创建者的创建的全部分享列表
GET /api/share/shares/:creatorId
// 分享创建者的全部分享返回
{
    "hisShares": [
        {
            "shareId": 1238272,
            "title": "分享一下Vue3.0",
            "desc": "Vue3.0就要发布了,带来了哪些新功能呢?",
            "where": "6F-19会议室",
            "startTime": 1548842400
        },
        {
            "shareId": 1238273,
            "title": "用flutter写app页面是一种什么样的体验",
            "desc":     "用跨平台框架flutter来写app页面的初体验", 
            "where": "6F-17会议室",
            "startTime": 1548842400
        },
        {
            "shareId": 1238274,
            "title": "Cordova原理",
            "desc": "一起来了解一下Cordova",
            "where": "6F-19会议室",
            "startTime": 1548842400
        }
    ]
}

Так что, если вы используете GraphQL?

query shareDetailPage($shareId: Int!, $creatorId:ID!, $start: Int!, $limit: Int = 10) {
    # 分享详情
    shareDetail: share (shareId: $shareId) {
        shareId: id
        title
        desc
        where
        logoUrl
        attchments
    }
    
    # 评论信息
    commentInfo(shareId: $shareId, start: $start, limit: $limit) {
        totalCount
        comments {
            id
            userId
            content
            commentTime
        }
    }
    
    # TA的分享
    hisShares (creatorId: $creatorId) {
        shares {
            title
            desc
            where
            startTime
        }
    }
}

Один запрос может сделать это.

операция мутации

Операция изменения, здесь представлен только один сценарий. Когда дело доходит до страницы сведений о совместном использовании, нам может потребоваться отредактировать совместное использование.Традиционным способом нам нужно вызвать интерфейс операции обновления:

POST /api/share/update/:shareId
FormData:
title=xxx&desc=xxx&where=xxx

После настройки этого интерфейса, чтобы подтвердить, что обновление прошло успешно, мы также можем снова вызвать интерфейс для получения и обмена данными:

GET /api/share/:shareId

Далее переключаемся на GraphQL:

mutation editShareInfo($shareObj: ShareInput!) {
    editShareInfo(shareInfo: $shareObj) {
    shareId: id
    title
    desc
    where
    logoUrl
    attchments
  }
}

Таким образом, вы можете напрямую изменять общий контент и возвращать измененные данные общего доступа.

другие функции

Для того, чтобы мы могли написать часть кода оператора запроса для лучшего повторного использования,GraphQLтакже обеспечиваетFragments(фрагмент),Inline Fragments(встроенный фрагмент) иDirectives(командная) функция. Первые два можно сравнить с JavaScriptfunction(функция) иanonymous function(анонимная функция),Directives(Инструкция) может решить, нужно ли возвращать некоторые поля в соответствии с параметрами, которые мы передаем. Я не буду вдаваться в подробности здесь.

Как достичь вышеуказанных функций?

schema

В приведенном выше примере определенно возникнут некоторые вопросы: как узнать, какие поля можно запрашивать? Какие параметры используются? Это требует введенияschema.

С точки зрения непрофессионала,schemaЭто соглашение, спецификация или может быть, когда он является интерфейсным документом.

GraphQL указывает, что каждыйschemaЕсть корневой (корневой) запрос и корневая (корневая) мутация.

Давайте сначала посмотрим, как написать корневой запрос, который по-прежнему является примером вышеприведенного запроса.

# 定义一个根查询
type Query {
    # 可以查询的字段和参数
    shares(start: Int = 0, limit: Int = 10, creatorId: ID): [Share!]!
    share(shareId: ID!): Share!
    commentInfo(shareId: ID!, start: Int = 0, limit: Int = 10): CommentInfo!
}

тип данных

Если вы знакомы с TypeScript или Flow, приведенные выше обозначения могут показаться вам знакомыми, да, смысл внутри — это то, что вы думаете. За параметром каждого поля, которое можно запросить, будет следовать тип параметра,!Используется для указания того, что этот параметр не может быть пустым.[]Это поле указывает на то, что запрос возвращает массив,[]Внутри находится тип массива.

Выше мы также видели некоторые типы, не существующие в TypeScript, такие какID,IDДавайте пока будем относиться к нему как к строкеStringтип в порядке. Подобно знакомому JavaScript или TypeScript,GraphQLТакже существует несколько базовых типов, вGraphQLони все вместе называются标量类型(Scalar Type), в том числе: Int (целое число), Float (число с плавающей точкой), String (строка), Boolean (логическое значение) и ID (уникальный тип идентификатора). в то же время,GraphQLТакже позволяют настроить Scalar Type, например: тип даты, просто реализуйте соответствующую сериализацию, обратная последовательность и проверку.

тип объекта

В приведенном выше определении корневого запроса мы также видим некоторые типы, связанные с бизнесом, такие как «Поделиться», «Комментарий», которые вместе называются对象类型. Тип объекта такжеGraphQLсерединаschemaОн может сказать нам, какие объекты доступны на сервисе и какие поля есть у этого объекта. Следующее, что нам нужно сделать, это определить эти типы объектов, пока все они не станут базовыми типами.

# 定义Share的对象类型
type Share {
  id: ID!
  title: String!
  desc: String!
  startTime: Int!
  where: String
  attchments: String
  logoUrl: String
  creatorId: ID!
  lastUpdateTime: Int
  is_delete: Int
  score: Int
  createTime: Int!
}

# 定义评论信息对象类型
type CommentInfo {
  totalCount: Int!
  comments: [Comment!]!
}

# 定义评论对象类型
type Comment {
  id: ID!
  content: String!
  commentTime: Int!
  userId: ID!
  shareId: ID!
}

Таким образом, мы завершили определение схемы.

Другие типы и функции

GraphQLНа самом деле естьEnumeration types(тип перечисления),Union types(тип сустава). В то же время, чтобы лучше повторно использовать код,GraphQLтакже обеспечиваетInterface(интерфейсная) функция. Я не буду вдаваться в подробности здесь.

реализация

GraphQLПо соглашению нам нужно предоставить по одному для каждого поля внутри корневого запроса и корневой мутации.resolverФункция. И оберните его в объект, чтобы выставить его, например:

const resolvers = {
    // 这里面写查询操作字段的resolver函数
    Query: {},
    // 这里面写变更操作字段的resolver函数
    Mutation: {},
}

export default resolvers

Давайте продолжим и напишем полное:

// 一些加载数据的async function
import { loadSharesFromDB, loadShareById, loadCommentsByShareId } from './datasource'

const resolvers = {
    // 这里面写查询操作字段的resolver函数
    Query: {
        shares: (parent, { start, limit, creatorId }, context, info) => {
            return loadSharesFromDB(start, limit, creatorId)
                    .then(...)
        },

        share: (parent, { shareId }, context, info) => {
            return loadShareById(shareId)
                    .then(...)
        },

        commentInfo: (parent, { shareId, start, limit }, context, info) => {
            return loadCommentsByShareId(shareId, start, limit)
                    .then(...)
        },
    },
    // 这里面写变更操作字段的resolver函数
    Mutation: {
         // ...
    },
}

Точно так же дляmutation(изменение) операции, мы также сначала ставимschemaЗаканчивать:

# 定义Mutation根入口
type Mutation {
  editShareInfo(shareInfo: ShareInput!): Share! 
}

input ShareInput {
  id: ID!
  title: String!
  desc: String!
  where: String
}

Затем завершитеresolverфункция:

import { updateShareInfo, loadShareById } from './datasource'

const resolvers = {
    Query: {
        // ...
    },
    
    Mutation: {
        editShareInfo: (parent, { shareInfo }, context, info) => {
            // 更新分享详情,then获取更新后的分享详情
            return updateShareInfo(shareInfo.id, shareInfo)
                    .then(loadShareById(shareInfo.id))
        },  
    },
}

export default resolvers

До сих пор мы достигли этого простогоGraphQLСервер поднят.

Эпилог

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

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

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

некоторые ссылки