- 基本用法
- GraphQL概述
- GraphQL基本語法特性
- GraphQL型別系統
- GraphQL型別系統內建基礎型別
- GraphQL型別系統內建修飾符
- GraphQL工作原理
- GraphQL執行過程
- Vue工程接入GraphQL
基本用法(如何去用)
package.json
"dependencies": {
"apollo-server-koa": "^1.3.6",
"graphql": "^0.13.2",
"graphql-import": "^0.6.0",
"graphql-tools": "^3.0.2",
"koa": "^2.5.1",
"koa-bodyparser": "^4.2.1",
"koa-router": "^7.4.0",
"koa-websocket": "^5.0.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.7.0"
}
複製程式碼
server.js
import koa from 'koa'
import koaRouter from 'koa-router'
import koaBody from 'koa-bodyparser'
import websocketify from 'koa-websocket'
import { graphqlKoa, graphiqlKoa } from 'apollo-server-koa'
import { makeExecutableSchema } from 'graphql-tools'
const app = websocketify(new koa())
const router = new koaRouter()
const PORT = 3000
// fake data
const moments = [
{
user: {
id: 1000,
name: '銳雯',
avatar: 'http://imgsrc.baidu.com/imgad/pic/item/42a98226cffc1e17d31927154090f603738de974.jpg'
},
main: {
content: '這是一條朋友圈',
pics: [
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1529219875063&di=bc0bcc78ae800c1c21c198f52697f515&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimgad%2Fpic%2Fitem%2F4a36acaf2edda3ccd53548ea0be93901203f9223.jpg',
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1529219893624&di=8d9e418df27e1fdb6afb1d993801a980&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimgad%2Fpic%2Fitem%2F3801213fb80e7beca9004ec5252eb9389b506b38.jpg'
]
},
comments: [
{
user: {
id: 233122,
name: '亞索'
},
reply: '面對疾風吧'
}
]
}
]
const typeDefs = `
type Query {
moments: [Moment]
}
type Mutation {
addComment(
entity: Add_Comment
) : Comment
}
type Moment {
user: User
main: Main
comments: [Comment]
}
type User {
id: Int
name: String
avatar: String
}
type Comment {
user: User
reply: String
}
type Main {
content: String
pics: [String]
}
input Add_User {
id: Int
name: String
}
input Add_Comment {
user: Add_User
reply: String
}
# 定義graphqlf服務哪個是RootQuery以及RootMutation
schema {
query: Query
mutation: Mutation
}
`
const resolvers = {
Query: {
moments () {
return moments
}
},
Mutation: {
addComment (_, { entity }, unknown, context) {
console.log(entity)
moments[0].comments.push(entity)
return entity
}
}
}
const schema = makeExecutableSchema({
typeDefs,
resolvers
})
// koaBody is needed just for POST.
router.post('/graphql', koaBody(), graphqlKoa({ schema: schema }))
// router.get('/graphql', graphqlKoa({ schema: schema }))
router.get('/graphiql', graphiqlKoa({ endpointURL: '/graphql' }))
async function responseMiddleware(ctx, next) {
ctx.set('Access-Control-Allow-Origin', 'http://localhost:8080')
ctx.set('Access-Control-Allow-Methods', 'POST,OPTIONS')
ctx.set('Access-Control-Allow-Headers', 'authorization,content-type')
// ctx.set('Access-Control-Allow-Credentials', 'true')
await next()
}
app.use(responseMiddleware)
app.use(router.routes())
app.use(router.allowedMethods())
app.ws.use(responseMiddleware)
app.ws.use(router.routes())
app.ws.use(router.allowedMethods())
app.listen(PORT)
複製程式碼
GraphQL概述
GraphQL基本語法特性
包括有fields,alias,arguments,fragments,variables,directives,inline fragments
- field
GraphQL型別系統
主要由RootQuery + RootMutation兩種入口型別(操作)加上RootValue(resolvers)構成GraphQL Schema。(此處用graphql-tools是為了將所有的型別定義在一個字串中,後續會移到一個.graphql檔案中,然後用graphql-import匯入)
GraphQL型別系統內建基礎型別
-
標量型別(Scalar Types)
Int
: 有符號的32位整數Float
: 有符號雙精度浮點值String
: UTF-8字元序列Boolean
: true or falseID
:ID 標量型別表示一個唯一識別符號(類似一種UUID),通常用以重新獲取物件或者作為快取中的鍵。ID 型別使用和 String 一樣的方式序列化。
-
列舉型別(Enumeration Types)
是一種特殊的標量型別
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
複製程式碼
- 陣列型別(Array Types)
用方括號[]
標記列表
- 介面型別(Interface Types)
是一種抽象型別,與java的interface機制類似。
- 聯合型別(Union Types)
{
search(text: "an") {
... on Human {
name
height
}
... on Droid {
name
primaryFunction
}
... on Starship {
name
length
}
}
}
複製程式碼
- 輸入型別(Input Types)
與之前提到的所有Types對立,這是一種也是唯一一種輸入型別,其主要用於mutations
時傳遞整個物件
的case,它沒有引數。
內建修飾符
!
: 表示非空。如下
query DroidById($id: ID!) {
droid(id: $id) {
name
}
}
複製程式碼
GraphQL工作原理
GraphQL中每個查詢欄位是返回子型別的父型別函式。每個型別的欄位對應由一個resolver
函式支援,當欄位被執行時,響應的resolver
被呼叫並return結果。
如果欄位產生結果為標量型別
值,比如字串或數字,則執行完成。否則遞迴
執行對應解析器直至結果為標量型別
值。
GraphQL基本資料流
每個GraphQL服務端應用的頂層必定會有一個入口點
,通常為Root或者Query型別,接著執行該欄位預設的解析器
(同步或非同步),而每個欄位被解析的結果被放置在鍵值對映中,欄位名(或別名)作為鍵,解析器的值作為值,這個過程從查詢欄位的底部葉子節點
開始返回,直到Query型別的起始節點
,最後生成映象查詢結果
返回給客戶端
Vue工程接入GraphQL
安裝vue-cli3.x
npm i -g @vue/cli
複製程式碼
初始化工程
vue create [project-name]
複製程式碼
引入apollo外掛
cd [project-name]
vue add apollo
複製程式碼
FriendCircle.vue
<template>
<div>
<div v-for="(item, index) in moments" :key="index">
{{item}}
</div>
<input type="text" v-model="comment.reply" placeholder="請輸入要回復的內容">
<button @click="addComment">回覆</button>
</div>
</template>
<script>
import gql from 'graphql-tag'
const QUERY_LIST = gql`
query {
moments {
user {
id
name
avatar
}
main {
content
pics
}
comments {
user {
id
name
}
reply
}
}
}
`
export default {
data () {
return {
moments: [],
comment: {
user: {
id: (Math.random() * 10000).toFixed(0),
name: '費德提克'
},
reply: ''
}
}
},
apollo: {
moments: {
query: QUERY_LIST
}
},
methods: {
addComment () {
this.$apollo.mutate({
mutation: gql`
mutation addComment($comment: Add_Comment) {
addComment(entity: $comment) {
user {
id
name
}
reply
}
}
`,
variables: {
comment: this.comment
},
update: (store, { data: { addComment } }) => {
// Read the data from our cache for this query.
const data = store.readQuery({ query: QUERY_LIST })
// set first moment's comment
data.moments[0].comments.push(addComment)
// Write our data back to the cache.
store.writeQuery({ query: QUERY_LIST, data })
}
})
}
}
}
</script>
複製程式碼
涉及的知識點有Root_Query,Root_Mutation,variables以及store cache
cache
其核心機制包括以下兩點
- 對所有(包括巢狀的)非標量型別遞迴進行快取,往往通過型別id或_id以及__typename唯一組合標識,然後在一個扁平的資料結構中儲存
- 可以設定不同快取策略:cache-and-network,no-cache,network-only
update回撥
this.$apollo.mutate(options) options中有一個update回撥,在成功響應資料後觸發,並且可以直接讀取並操作由apollo-cache-inmemory
生成的store。上述例子中使用此回撥同步更新快取以及UI
注:所有繫結的變數均不可直接修改,內部使用Object.freeze將物件凍結,無法直接增刪。