从GraphQL到前端数据管理的革命 - GraphQL干货笔记

本文涉及的产品
数据管理 DMS,安全协同 3个实例 3个月
推荐场景:
学生管理系统数据库
简介: 你知道吗?FaceBook、GitHub,Pinterest,Twitter,Sky,纽约时报,Shopify,Yelp这些大公司已经在使用GraphQL规范的接口规范了。再不学习就落后了。

代码GitHub链接


参考文章






你将Get到的技能


  • GraphQL概念


  • 实战GraphQL - Query/Mutation/Subscription


  • Rest vs GraphQL 对比分析


  • 如何搭建GraphQL后端服务


  • 如何利用React hooks 、Provider封装GraphQL客户端


  • 如何通过GraphQL完成对数据变化的订阅


SPA框架崛起带来的问题


在前端的开发中是否会遇到这样的困扰?


1. 面对复杂场景的API粒度问题


  • 减少请求次数就需要合并请求


  • 多端应用Web、App、小程序视图不同所以需要的接口也不同


  • API接口粒度难于确定


  • 粗粒度:移动端不必要的流量耗损


  • 细粒度:造成函数爆炸 (Function Explosion)


1. API版本划分问题


  • 需要频繁应对API版本的演进


2. 实现双向通讯时接口风格不统一


如果需要实现支付状态、或者多人协作 、实时同步股票信息向客户端推送数据时,往往需要使用WebSocket通讯或者其他通讯方式。这时你会发现如果向服务器请求使用Restful风格无法保证接口风格统一。


3. 组件需要各自管理状态


  • 组件需要将异步请求状态分发


  • 父子组件通讯使结构复杂


  • 订阅的数据响应会使得数据流变得杂乱无章可读性变差


GraphQL概览


英文:graphql.org/


中文: graphql.cn/


Github GraphQL Explorerdeveloper.github.com/v4/explorer…


概念


  • GraphQL 是由 Facebook 创造的用于 API 的查询语言。


  • 前后端数据查询方式的规范。


GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。


优势


  • 精确定义所需数据的能力(GraphQL拥有强类型)


# 查询
query {
  book(id: "1") {
    id,
    author,
  }
}
# 结果
{
  "data": {
    "book": {
      "id": "1",
      "author": "Author1"
    }
  }
}


  • GraphQL允许您通过一次调用替换多个REST请求以获取指定的数据


  • 描述所有可能类型的系统


# 查询
query {
  book(id: "1") {
    id,
    author,
  },
  book2 : book(id:"3"){
    id
  }
}
# 结果
{
  "data": {
    "book": {
      "id": "1",
      "author": "Author1"
    },
    "book2": {
      "id": "3"
    }
  }
}


  • API 演进无需划分版本


给你的 GraphQL API 添加字段和类型而无需影响现有查询。老旧的字段可以废弃,从工具中隐藏。通过使用单一演进版本,GraphQL API 使得应用始终能够使用新的特性,并鼓励使用更加简洁、更好维护的服务端代码。


GraphQL VS Restful


扩展阅读 REST, GraphQL, Webhooks, & gRPC 如何选型


GraphQL Restful
一次请求多资源 ✔️
API字段定制化 ✔️
精确定义返回类型 ✔️
无需划分版本 ✔️
类型验证机制 ✔️
支持双向通讯 ✔️


基础操作


语法说明 graphql.cn/learn/queri…


[进阶阅读复杂语法 Fragments Directives Function] juejin.cn/post/684490…


Query


普通查询


query {
  books {
    title,
    author
  }
}
### Result
{
  "data": {
    "books": [
      {
        "title": "abc",
        "author": "xxxx"
      }
    ]
  }
}


带有参数和变量和别名的查询


# 查询
 query($id:String) {
  book(id: $id) {
    id,
    author,
  },
  book2 : book(id:"3"){
    id
  }
}
# 变量
{
  "id":"1"
}
# 结果
{
  "data": {
    "book": {
      "id": "1",
      "author": "Author1"
    },
    "book2": {
      "id": "3"
    }
  }
}



Mutation


对于数据改变这种非幂等性操作使用Mutation来进项描述。


REST 中,任何请求都可能最后导致一些服务端副作用,但是约定上建议不要使用 GET 请求来修改数据。GraphQL 也是类似 —— 技术上而言,任何查询都可以被实现为导致数据写入。然而,建一个约定来规范任何导致写入的操作都应该显式通过变更(mutation)来发送。


# 查询
mutation {
  createBook(title:"TTTT",author: "AAA") {
    id
  }
}


Subscription


如果数据发生变化希望后台主动通知前端,你可以使用Subscription后台消息订阅功能。


subscription  {
  subsBooks
}



ApolloServer后端


ApolloServer是一个开源的GraphQL框架。ApolloServer可以单独的作为服务器,同时ApolloServer也可以作为Express,Koa等Node框架的插件。


HelloWorld


const { ApolloServer, gql } = require('apollo-server');
// Schema定义
const typeDefs = gql`
  type Query {
    hello: String,
  }
`;
// 解释器实现
const resolvers = {
    Query: {
        hello: () => 'Hello world!',
    }
};
// 创建服务器实例
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
    console.log(`🚀  Server ready at ${url}`);
});


Schema定义小结


数据类型


GraphQL也有几个基础类型,在GraphQL中他们统称叫标量类型(Scalar Type)


  • Int(整型)


  • Float(浮点型)


  • String(字符串)


  • Boolean(布尔型)


  • ID(唯一标识符类型)


  • 自定义类型 例如:Date类型,只需实现相关的序列化,反序列化和验证的功能即可


对象类型


我们可以根据需要将数据类型组合为对象这些统称为对象类型。


type Book {
    id:String
    title: String
    author: String
  }


其他类型


为了达到更好的代码复用GraphQl还提供更为复杂的接口类型这里面就不在一一赘述。


  • Enumeration types(枚举类型)


  • Union types(联合类型)


  • Interface(接口)


Query


// index.js
// 添加Schema
const typeDefs = gql`
  type Query {
    books: [Book],
    book(id : String) : Book
  }
  type Book {
    id:String
    title: String
    author: String
  }
`;
// 创建数据
const books = (
    () => Array(5).fill().map((v, i) => ({
        id: '' + i,
        title: 'Title' + i,
        author: 'Author' + i
    }))
)()
// 添加resolve
const resolvers = {
    Query: {
        books: () => books,
        book: (parent, { id }) => {
            return books.find(v => v.id === id)
        }
    },
}


Mutation


const typeDefs = gql`
  type Mutation {
    createBook(title: String, author: String): Book!,
    clearBook : Boolean
  }
`
resolvers.Mutation = {
    createBook: (parent, args) => {
        const book = { ...args, id: books.length + 1 + '' }
        books.push(book)
        return book
    },
    clearBook: () => {
        books.length = 0
        return true
    }
 }


Subscription


const { ApolloServer, gql, PubSub, withFilter } = require('apollo-server');
const typeDefs = gql`
  type Subscription {
    subsBooks : Boolean,
  }
`;
const pubsub = new PubSub()
resolvers.Mutation = {
    createBook: (parent, args) => {
        const book = { ...args, id: books.length + 1 + '' }
        books.push(book)
        // 发布订阅消息
        pubsub.publish('UPDATE_BOOK', {
            subsBooks: true
        })
        return book
    },
    clearBook: () => {
        books.length = 0
        // 发布订阅消息
        pubsub.publish('UPDATE_BOOK', {
            subsBooks: true
        })
        return true
    }
}
resolvers.Subscription = {
    subsBooks: {
        // 过滤不需要订阅的消息
        subscribe: withFilter(
            (parent, variables) => pubsub.asyncIterator('UPDATE_BOOK'),
            (payload, variables) => true
        )
    },
}


GraphQL客户端通讯(Axios)


Query


<script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script>
<script>
  axios
    .post("http://localhost:4000/graphql", {
      query: `query {
        books {
          id
          title
          author
        }
      }`
    })
    .then(res => {
      console.log("res: ", res);
      document.writeln(JSON.stringify(res.data))
    });
</script>


Mutataion


<script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script>
<script>
  axios
    .post("http://localhost:4000/graphql", {
      query: `mutation($title:String,$author:String) {
        createBook(title:$title,author:$author){
          id
        }
      }`,
      variables: {
        title: "TTTTT",
        author: "AAAAA"
      }
    })
    .then(res => {
      console.log("res: ", res);
      document.writeln(JSON.stringify(res.data))
    });
</script>


GraphQL响应式数据应用


[响应式 GraphQL 结构


](github.com/xitu/gold-m…)


ApolloClient (React)实现全局状态管理


参考文章 www.zcfy.cc/article/the…


前端数据管理Redux(指令时) vs Apollo(声明式)



在使用Apollo时我们可以尝试一种完全不同的前端数据管理方式,即声明式数据管理。在传统的项目中我们通常会将数据存放在Redux这样的统一状态管理模块中。利用ApolloClient通过GraphQL数据声明的方式管理数据。每个模块都可以根据自己的需求定制好自己想要的数据。


数据连接Provider


const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
});
ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode >,
  document.getElementById('root')
);


需要使用Subscription的时候需要符合连接


// Subscription
// Create an http link:
const httpLink = new HttpLink({
  uri: 'http://localhost:4000/graphql'
});
// Create a WebSocket link:
const wsLink = new WebSocketLink({
  uri: `ws://localhost:4000/graphql`,
  options: {
    reconnect: true
  }
});
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
  // split based on operation type
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);
// Subscription
const cache = new InMemoryCache();
const client = new ApolloClient({
  link,
  cache
});


Query


import React, { useEffect } from 'react';
import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const QUERY = gql`
    query {
        books {
            id,
            author,
            title
        }
    }
`;
function Query() {
    const { loading, error, data, refetch } = useQuery(QUERY)
    useEffect(() => {
        refetch()
    })
    if (loading) return <p>Loading...</p>
    if (error) return <p>Error :(</p>
    console.log('book', data)
    const list = data.books.map(v => (
    <div>{v.author}: {v.title}</div>
    ))
    return list
}
export default Query;


Mutation


import React from 'react';
import { useMutation } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const CREATE_BOOK = gql`
    mutation CreateBook($title:String!,$author:String!){
        createBook(title:$title,author:$author){
            id,
            title,
            author
        }
    }
`;
const CLEAR_BOOK = gql`
    mutation {
        clearBook
    }
`;
function Mutation() {
    const [create, { data }] = useMutation(CREATE_BOOK);
    const [clear] = useMutation(CLEAR_BOOK)
    return (
        <div>
            <form
                onSubmit={e => {
                    e.preventDefault();
                    create({
                        variables: {
                            "title": 'Title' + (Math.random() * 100).toFixed(),
                            "author": 'Author'+ (Math.random() * 100).toFixed()
                        }
                    });
                    console.log('mutation:',data)
                }}
            >
                <button type="submit">Create</button>
            </form>
            <button onClick={ clear }>Clear</button>
        </div>
    );
}
export default Mutation;


Subscription


import React from 'react';
import { useSubscription } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
import Query from './Query'
const subs = gql`
    subscription {
        subsBooks
    }
`;
function Subscription() {
    useSubscription(subs)
    return <Query/>
}
export default Subscription;


附录


Apollo资源参考这篇文章 juejin.cn/post/684490…


  • 服务端



  • graph-pack 支持热更新的零配置 GraphQL 服务环境


  • 客户端


  • Relay   Facebook 的 GraphQL 工具。


  • Prisma 弥合了数据库和GraphQL resolvers之间的鸿沟,让实现生产级别的GraphQL服务器变得更加容易。 除了强大的查询引擎和API,Prisma在开发体验方面尤为突出。www.prisma.io/


  • typeorm 直接复用typegraphql中创建的model


相关实践学习
MySQL基础-学生管理系统数据库设计
本场景介绍如何使用DMS工具连接RDS,并使用DMS图形化工具创建数据库表。
相关文章
|
28天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
118 2
|
8天前
|
前端开发 JavaScript 开发者
颠覆传统:React框架如何引领前端开发的革命性变革
【10月更文挑战第32天】本文以问答形式探讨了React框架的特性和应用。React是一款由Facebook推出的JavaScript库,以其虚拟DOM机制和组件化设计,成为构建高性能单页面应用的理想选择。文章介绍了如何开始一个React项目、组件化思想的体现、性能优化方法、表单处理及路由实现等内容,帮助开发者更好地理解和使用React。
33 9
|
1月前
|
前端开发 JavaScript API
探索React Hooks:前端开发的革命性工具
【10月更文挑战第5天】探索React Hooks:前端开发的革命性工具
|
28天前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
36 0
|
3天前
|
前端开发 JavaScript
前端界的革命:掌握这些新技术,让你的代码简洁到让人惊叹!
前端技术的快速发展带来了许多令人惊叹的新特性。ES6及其后续版本引入了箭头函数、模板字符串等简洁语法,极大减少了代码冗余。React通过虚拟DOM和组件化思想,提高了代码的可维护性和效率。Webpack等构建工具通过模块化和代码分割,优化了应用性能和加载速度。这些新技术正引领前端开发的革命,使代码更加简洁、高效、可维护。
9 2
|
10天前
|
前端开发 JavaScript API
前端界的革命性突破:掌握这些新技术,让你的作品引领潮流!
【10月更文挑战第30天】前端技术日新月异,从传统的HTML、CSS、JavaScript到现代的React、Vue、Angular等框架,以及Webpack、Sass等工具,前端开发经历了巨大变革。本文通过对比新旧技术,展示如何高效掌握这些新技术,助你作品引领潮流。
23 2
|
14天前
|
Rust 前端开发 JavaScript
前端性能革命:WebAssembly在高性能计算中的应用探索
【10月更文挑战第26天】随着Web应用功能的日益复杂,传统JavaScript解释执行模式逐渐成为性能瓶颈。WebAssembly(Wasm)应运而生,作为一种二进制代码格式,支持C/C++、Rust等语言编写的代码在浏览器中高效运行。Wasm不仅提升了应用的执行速度,还具备跨平台兼容性和安全性,显著改善了Web应用的响应速度和用户体验。
29 4
|
28天前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
28天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
28天前
|
机器学习/深度学习 弹性计算 自然语言处理
前端大模型应用笔记(二):最新llama3.2小参数版本1B的古董机测试 - 支持128K上下文,表现优异,和移动端更配
llama3.1支持128K上下文,6万字+输入,适用于多种场景。模型能力超出预期,但处理中文时需加中英翻译。测试显示,其英文支持较好,中文则需改进。llama3.2 1B参数量小,适合移动端和资源受限环境,可在阿里云2vCPU和4G ECS上运行。