標量(ScalarTypeDefinition)是 GraphQL 中不可分割的原子數據類型,在服務中充當葉子節點。對於客戶端而言,合法的查詢集(Select Set)必須到達葉子節點,也就是到達標量類型節點。javascript
GraphQL 規範提供了五種標量:java
UTF-8
字符序列上述五種類型與其餘語言對應的類型定義類似,相信讀者老爺們都已經很是熟悉,無需贅述,惟一值得探討的是 ID
類型。git
ID
類型只是一個字符串格式的值,引擎支持字符串解析值,也支持將 Int
解析值轉換爲字符串類型;ID
" 類型應該用於惟一標誌一個資源對象,也就是說,使用相同 ID 值,不管查詢多少次,結果都應該是同一對象,這一點有助於實現緩存,是 GraphQL 推薦的緩存方案;咱們來看一下例子加深印象:github
[
// 字符串類型
{id: '1'},
// int 類型,引擎會將其轉換爲字符串
{id: 1},
// float 類型
// 非法值,引擎不支持float轉換
// 將拋出 `TypeError` 錯誤
{id: 1.2},
// 與上面第一條重複
// 合法值,引擎並不強制 `ID` 值的惟一性
{id: '1'}
]
複製代碼
除規範定義的標量外,還能夠按需定義業務範疇內的標量。語法很是簡單:緩存
scalar Datetime
複製代碼
注意,這只是語義範疇定義,還須要定義序列化、反序列化函數:函數
new GraphQLScalarType({
name: "Datetime",
description: "日期時間標量類型",
// 序列化函數
serialize(value) {
return value.toString();
},
// 解析函數
parseValue(value) {
if (typeof value === "string") {
return new Date(value);
}
throw new Error("參數類型錯誤");
},
// 解析函數
parseLiteral(ast) {
if (ast.kind === Kind.STRING) {
return new Date(ast.value);
}
throw new Error("參數類型錯誤");
}
});
複製代碼
下面咱們一個一個看這些配置:工具
name
: 字段名,請保持與 schema 中定的標量類型名稱保持一致description
: 類型描述,在一些診斷工具上仍是頗有用的serialize
: 序列化函數,用於將結果轉換爲適合 http 傳輸的數值類型parseValue
: 解析函數,用於將客戶端經過 variables 參數傳遞的數值爲 Date 類型parseLiteral
: 一樣是解析函數,將客戶端傳遞的 字面量參數 解析爲 Date 類型配置中的 parseValue
、parseLiteral
兩個函數功能上類似,都用於解析客戶端參數,分別處理兩種參數傳遞方式:ui
# variables 參數
# 引擎將調用 parseValue 函數
query (before: Datetime){
users(before: $before) {
id
name
}
}
variables {
before: "1991-02-19"
}
# 字面量參數
# 引擎將調用 parseLiteral 函數
query {
users(before: "1991-02-19") {
id
name
}
}
複製代碼
最後說一些注意的點:spa
InputType
,能夠省略 parseValue
、parseLiteral
。parseValue
接收到的是 variables
對象中對應的值;而 parseLiteral
接收的則是引擎從 query
語句中解析出的 AST 節點。AST 節點內容形如:{
// 字面量類型
"kind": "StringValue",
// 字面量值
"value": "1991-02-19",
// 指明字面量是否爲 [BlockStringValue](https://facebook.github.io/graphql/June2018/#BlockStringValue()) 類型
"block": false,
// token 位置
"loc":
{
"start": 18,
"end": 30
}
}
複製代碼
標量類型也支持返回結構化的對象,只要能爲引擎提供符合規則的 serialize
函數,一切皆有可能。咱們能夠寫出這樣一個標量:scala
// Address 對象類型,不過這是一個標量
new GraphQLScalarType({
name: "Address",
description: "對象類型的標量",
serialize(value) {
// value 爲對象類型
// value = { city: '深圳', province: '廣東省', country: '中國' }
return value;
}
});
複製代碼
可是要注意,標量類型是 不可分割 的,不能再傳入查詢子集:
# 合法請求
query {
users {
id
name
# Address 類型值
bornOrigin
}
}
複製代碼
返回結果:
{
"data": {
"users": [
{
"id": "1",
"name": "foo",
"bornOrigin": {
"city": "深圳",
"province": "廣東省",
"country": "中國"
}
}
]
}
}
複製代碼
完整代碼在 此處。 雖然合乎規則,但用 標量類型 來返回一個沒法被拆解的對象,違反了 按需加載 這一重要原則,並不值得推崇,除非實在找不到更好的解決方案。 好比,有時候咱們須要處理高度動態的信息結構,咱們指望以結構化、可預期的形式傳輸信息,此時咱們就不得不採用這種方案了。 以日誌爲例,一個稍上規模的系統,日誌格式多種多樣,若是要一一枚舉,一一轉化成 GraphQL 的 SDL,開發、維護成本都很是高,那用一個標量類型表示這多種多樣的格式,性價比就很高了。
標量是 GraphQL 中的原子類型,通常充當查詢的葉子節點。 GraphQL 規範提供了五種標量類型,其中 ID
最爲特殊,用於惟一標誌一個資源實例。 在標準標量以外,也能夠按需定義新的標量,規則如上。