GraphQL——由Facebook創(chuàng)建的接口規(guī)范,用于API的查詢,為前后端數(shù)據(jù)交互提供了新的查詢方式。
一、RESTFul的痛點
1、面對復雜場景的API粒度設計問題
在多端應用開發(fā)需求時,如Web、App、小程序... Web頁面所需的描述字段往往更多一點,為滿足web頁面的接口需求,對于App/小程序而言,接口字段是冗余的,這就造成了流量浪費、產(chǎn)生性能問題~ 同理,為了提高加載速度,通過合并減少請求次數(shù)的方式,也會生成同樣問題。
-
粗粒度設計
流量性能浪費:造成移動端不必要的流量損耗; -
細粒度設計
大量接口產(chǎn)生:不同端、不同頁面、不同接口、同一頁面多次請求造成函數(shù)爆炸;
2、API版本規(guī)劃問題
接口字段等可能變動,之類的版本迭代問題
3、雙向通訊的需求
由于http只能由瀏覽器向服務器單向推送消息,如股票等信息實時推送、支付等功能無法實現(xiàn),雖然可以通過websocket實現(xiàn),但不同的通信協(xié)議,接口規(guī)范無法統(tǒng)一表現(xiàn)風格
4、對于組件需要各自管理狀態(tài)的難點
目前程序通常都是通過使用統(tǒng)一狀態(tài)管理工具,對于實現(xiàn)組件各自的狀態(tài)管理這種新的編碼風格相對比較麻煩。
二、三個核心功能及工作方式
- Queries :提供查詢類接口
- Mutations: 處理狀態(tài)的變化
- Subscriptions: 訂閱后端狀態(tài)變化、通知前端
1、Queries功能很類似解構賦值
query {
hello,
hi: hello, // 前端可以通過別名使用
books(id:'1'), // 只從books中查找到id為1的數(shù)據(jù)
books(id:'1'){ // 只從books中查id為1的date、author
date,
author
}
}
任意字段、對應別名均可自由定制,從而能避免接口版本更新問題,前端不會受后端接口版本變化而影響。
2、Mutations主要處理增刪改邏輯
mutation {
createBook( name:'創(chuàng)建新書', author:'作者' ) {
id // id等其他信息
}
}
3、Subscriptions前端訂閱后端發(fā)送的消息
subscription{
subsBooks
}
前端可以自由定制接口,后端可以按需返回,基于websocket實現(xiàn)的。
三、開發(fā)后端程序主要用Apollo Server框架
Apollo Server可以單獨作為服務器,也可以作為Express、Koa的插件被使用。實例基礎:
const { ApolloServer,gql } = require('apollo-server');
// Schema_gql接口定義
const typeDefs = gql`
type Query {
hello: String
}
`
// 解釋器的實現(xiàn)
const resolvers = {
Query: {
hello: ()=>'Hello World'
}
}
// 創(chuàng)建服務器實例
const server = new ApolloServer({typeDefs,resolvers});
// 啟動
server.listen().then( ({url}) => { console.log('運行會重新啟動playground') } );
詳例開發(fā)圖書的查詢接口:
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 // 是否清空成功
}
`
// 創(chuàng)建數(shù)據(jù)_Book列表,是一個匿名函數(shù)
const books = ( ()=>{
Array(6).fill()
.map( (v,i)=>({
id: 'book'+i,
name: 'Name'+i,
author: 'Author'+i
}) )
} )();
// 解釋器的實現(xiàn)
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;
}
}
}
// 創(chuàng)建服務器實例
const server = new ApolloServer({typeDefs,resolvers});
// 啟動
server.listen().then( ({url}) => { console.log('運行會重新啟動playground') } );
Subscription的用法:
const { PubSub, withFilter} = require('apollo-server');
// PubSub為訂閱發(fā)布模式
const typeDefs = gql`
type Subscription{
subsBook: Boolean
}
`
// 創(chuàng)建訂閱發(fā)布實例
const pubsub = new PubSub();
// 解釋器中添加
const resolvers = {
Subscripion: {
subsBooks:{
subscribe: withFilter(
( parent, variables ) => pubsub.asyncIterator('UPDATE_BOOK'),
() => true // 過濾UPDATE_BOOK消息、發(fā)布true
)
}
},
// 發(fā)布消息訂閱在Mutation中
Mutation: {
createBook: ( parent,args ) => {
const book = { ...args,id:books.length+1+''};
books.push(book);
pubsub.publish('UPDATE_BOOK',{
subsBooks: true
})
return book;
},
}
}
四、前端調(diào)用訂閱(示React用法)
import React from 'react';
import { useMutation } from '@/apollo/react-hooks'; // 借用useMutation鉤子實現(xiàn)
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
}
`
// 創(chuàng)基函數(shù)式組件
function Mutation {
// 導出create、clear方法,執(zhí)行時會調(diào)用mutation向后端創(chuàng)建數(shù)據(jù)
const [ create, {data} ] = useMutation(CREATE_BOOK);
const [ clear ] = useMutation(CLEAR_BOOK);
// 頁面且放兩個按鈕
return (
<div>
{/* 每次點擊都會創(chuàng)建新數(shù)據(jù) */}
<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>加載即可
用聲明式數(shù)據(jù)管理取代統(tǒng)一管理的方式,可以使用useSubscription鉤子實現(xiàn)數(shù)據(jù)訂閱,為前后端交互提供新的可能,在每個組件內(nèi)部訂閱數(shù)據(jù)的狀態(tài),寫法更簡單明確、用法更容易~