GraphQL图形可视化

2022-06-30  本文已影响0人  小石头糖糖

GraphQL——由Facebook创建的接口规范,用于API的查询,为前后端数据交互提供了新的查询方式。

一、RESTFul的痛点
1、面对复杂场景的API粒度设计问题

在多端应用开发需求时,如Web、App、小程序... Web页面所需的描述字段往往更多一点,为满足web页面的接口需求,对于App/小程序而言,接口字段是冗余的,这就造成了流量浪费、产生性能问题~ 同理,为了提高加载速度,通过合并减少请求次数的方式,也会生成同样问题。

2、API版本规划问题

接口字段等可能变动,之类的版本迭代问题

3、双向通讯的需求

由于http只能由浏览器向服务器单向推送消息,如股票等信息实时推送、支付等功能无法实现,虽然可以通过websocket实现,但不同的通信协议,接口规范无法统一表现风格

4、对于组件需要各自管理状态的难点

目前程序通常都是通过使用统一状态管理工具,对于实现组件各自的状态管理这种新的编码风格相对比较麻烦。

二、三个核心功能及工作方式
1、Queries功能很类似解构赋值
query {
  hello,
  hi: hello,  // 前端可以通过别名使用
  books(id:'1'),  // 只从books中查找到id为1的数据
  books(id:'1'){  // 只从books中查id为1的date、author
    date,
    author
  } 
}

任意字段、对应别名均可自由定制,从而能避免接口版本更新问题,前端不会受后端接口版本变化而影响。

2、Mutations主要处理增删改逻辑
mutation {
  createBook( name:'创建新书', author:'作者' ) {
    id  // id等其他信息
  }
}
3、Subscriptions前端订阅后端发送的消息
subscription{
  subsBooks
}

前端可以自由定制接口,后端可以按需返回,基于websocket实现的。

三、开发后端程序主要用Apollo Server框架

Apollo Server可以单独作为服务器,也可以作为Express、Koa的插件被使用。实例基础:

const { ApolloServer,gql } = require('apollo-server');

// Schema_gql接口定义
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('运行会重新启动playground') } );

详例开发图书的查询接口:

const { ApolloServer,gql } = require('apollo-server');

// Schema_gql接口定义
const typeDefs = gql`
    type Query {
        book: [Book], // Book为新的自定义类型
        books( id:String ):Book
    }
    type Book {
        id: String,
        name: Sting,
        author: String
    }

    type Mutation {
        createBook( name:String, author:String ): Books!,  // 必须返回
        clearBook: Boolean // 是否清空成功
    }
`
// 创建数据_Book列表,是一个匿名函数
const books = ( ()=>{
    Array(6).fill()
    .map( (v,i)=>({
        id: 'book'+i,
        name: 'Name'+i,
        author: 'Author'+i
    }) )
} )();

// 解释器的实现
const resolvers = {
    Query: {
        // hello: ()=>'Hello World'
        books: ()=> books,
        book: ( parent,{id} )=>{
            return books.find( v=> v.id===i );
        }
    },
    Mutation: {
        createBook: ( parent,args ) => {
            const book = { ...args,id:books.length+1+''};
            books.push(book);
            return book;
        },
        clearBook: () => {
            books.length = 0;
            return true;
        }
    }
}

// 创建服务器实例
const server = new ApolloServer({typeDefs,resolvers});

// 启动
server.listen().then( ({url}) => { console.log('运行会重新启动playground') } );

Subscription的用法:

const { PubSub, withFilter} = require('apollo-server');
// PubSub为订阅发布模式

const typeDefs = gql`
    type Subscription{
        subsBook: Boolean
    }
`

// 创建订阅发布实例
const pubsub = new PubSub();

// 解释器中添加
const resolvers = {
    Subscripion: {
        subsBooks:{ 
            subscribe: withFilter(
                ( parent, variables ) => pubsub.asyncIterator('UPDATE_BOOK'),
                () => true  // 过滤UPDATE_BOOK消息、发布true
            )
        }
    },
    // 发布消息订阅在Mutation中
    Mutation: {
        createBook: ( parent,args ) => {
            const book = { ...args,id:books.length+1+''};
            books.push(book);
            pubsub.publish('UPDATE_BOOK',{
                subsBooks: true
            })
            return book;
        },
    }
}
四、前端调用订阅(示React用法)
import React from 'react';
import { useMutation } from '@/apollo/react-hooks'; // 借用useMutation钩子实现
import { gql } from 'apollo-boost';

// 第一个查询
const CREATE_BOOK = gql`
    mutation CreateBook( $name: String!, $author: String! ){
        createBook( name: $name, author: $author ){
            id,
            name,
            author
        }
    }
`

// 第二个查询
const CLEAR_BOOK = gql`
    mutation {
        clearBook
    }
`

// 创基函数式组件
function Mutation {
    // 导出create、clear方法,执行时会调用mutation向后端创建数据
    const [ create, {data} ] = useMutation(CREATE_BOOK);
    const [ clear ] = useMutation(CLEAR_BOOK);

    // 页面且放两个按钮
    return (
        <div>
            {/* 每次点击都会创建新数据 */}
            <form onSubmit={ e=> {
                e.preventDefault();
                create({
                    variables:{
                        'name': 'Name' + ( Math.random()*100 ).toFixed(),
                        'author': 'Author' + ( Math.random()*100 ).toFixed()
                    }
                })
            } }></form>
            <button onClick = {clear}> Clear </button>
        </div>
    )
}

export default Mutation;
五、使用

引入Mutation控件、<Mutation></Mutation>加载即可

用声明式数据管理取代统一管理的方式,可以使用useSubscription钩子实现数据订阅,为前后端交互提供新的可能,在每个组件内部订阅数据的状态,写法更简单明确、用法更容易~

上一篇下一篇

猜你喜欢

热点阅读