跳至主要內容
版本:v18.0.0

@alias 指令

@alias 指令可讓您將擴展片段(無論是具名的片段擴展或行內片段)作為選擇範圍內的具名字段公開。這允許 Relay 在片段的類型可能與父選擇不符的情況下提供額外的類型安全。

資訊

本文說明引入 @alias 指令的原因,以及如何使用它來提高 Relay 應用程式中的類型安全。 若要了解其 API,請參閱 API 參考

讓我們看看一些 @alias 可以派上用場的範例

抽象類型

假設您有一個元件可呈現 Viewer 的相關資訊

function MyViewer({viewerKey}) {
const {name} = useFragment(graphql`
fragment MyViewer on Viewer {
name @required(action: THROW)
}`, viewerKey);

return `My name is ${name}. That's ${name.length} letters long!`;
}

若要在具有 Node (Viewer 實作) 片段的元件中使用該元件,您可以撰寫如下內容

function MyNode({nodeKey}) {
const node = useFragment(graphql`
fragment MyFragment on Node {
...MyViewer
}`, nodeKey);

return <MyViewer viewerKey={node} />
}

您發現問題了嗎?我們實際上不知道傳遞給 <MyViewer /> 的節點實際上是不是 Viewer <MyViewer />。如果 <MyNode /> 嘗試呈現 Comment (也會實作 Node),則會在 <MyViewer /> 中發生執行階段錯誤,因為該欄位名稱不存在於 Comment 上。

TypeError: Cannot read properties of undefined (reading 'length')

我們不僅沒有獲得任何類型來讓我們知道這個潛在問題,甚至在執行階段也無法檢查節點是否實作了 Viewer,因為 Viewer 是抽象類型!

別名片段

別名片段可以解決此問題。以下是使用別名片段時 <MyNode /> 的樣子

function MyNode({nodeKey}) {
const node = useFragment(graphql`
fragment MyFragment on Node {
...MyViewer @alias(as: "my_viewer")
}`, nodeKey);

// Relay returns the fragment key as its own nullable property
if(node.my_viewer == null) {
return null;
}

// Because `my_viewer` is typed as nullable, Flow/TypeScript will
// show an error if you try to use the `my_viewer` without first
// performing a null check.
// VVVVVVVVVVVVVV
return <MyViewer viewerKey={node.my_viewer} />
}

透過這種方式,您可以看到 Relay 將片段索引鍵公開為自己的可為 null 的屬性,這讓我們可以檢查節點是否真的有實作 Viewer,甚至允許 Flow 強制元件處理這種可能性!

@skip 和 @include

在片段上使用 @skip@include 指令時,可能會發生類似的問題。為了安全地使用擴展片段,您需要檢查是否已擷取該片段。過去,這需要存取用來判斷是否跳過或包含該片段的查詢變數。

透過 @alias,您現在可以透過簡單地為片段指派別名,並檢查別名是否為 null 來檢查是否已擷取該片段

function MyUser({userKey}) {
const user = useFragment(graphql`
fragment MyFragment on User {
...ConditionalData @skip(if: $someVar) @alias
}`, userKey);

if(user.ConditionalData == null) {
return "No data fetched";
}
return <ConditionalData userKey={user.ConditionalData} />
}

強制安全

我們已概述兩種不同的方式,在沒有 @alias 的情況下,片段在目前的 Relay 中可能不安全。為了避免這些不安全邊緣案例導致執行階段問題,Relay 將很快要求所有有條件擷取的片段都要有別名。

若要立即在專案中試用此驗證,您可以為專案啟用實驗性的 enforce_fragment_alias_where_ambiguous 編譯器功能旗標。為了實現逐步採用此強制執行,Relay 公開了 @dangerously_unaliased_fixme 指令,這將會抑制這些強制執行錯誤。這將允許您為所有新的擴展啟用強制執行,而無需先遷移所有現有的問題。

Relay VSCode 擴充功能提供快速修正,可將 @alias@dangerously_unaliased_fixme 新增至不安全的片段。

與 @required 搭配使用

@alias 可以與 @required(action: NONE) 搭配使用,以將必要欄位分組在一起。在以下範例中,我們將 nameemail 分組為 requiredFields。如果任一欄位為 null,則該 null 值會向上冒泡至 user.requiredFields 欄位,使其為 null。這讓我們可以執行單一檢查,而不會影響 id 欄位。

function MyUser({userKey}) {
const user = useFragment(graphql`
fragment MyFragment on User {
id
... @alias(as: "requiredFields") {
name @required(action: NONE)
email @required(action: NONE)
}
}`, userKey);

if(user.requiredFields == null) {
return `Missing required fields for user ${user.id}`;
}
return `Hello ${user.requiredFields.name} (${user.requiredFields.email}).!`;
}
注意

目前不支援在具有 @alias 的片段擴展上使用 @required,但我們可能會在未來新增支援。

底層原理

對於熟悉 Relay 或有興趣學習的人員,以下簡要說明此功能的實作方式

在底層中,@alias 完全在 Relay (編譯器和執行階段) 中實作。它不需要任何伺服器支援。Relay 編譯器會解譯 @alias 指令,並產生類型,指出片段索引鍵或行內片段資料將附加到新欄位,而不是直接附加到父物件。在 Relay 執行階段成品中,它會使用一個新節點包裝片段節點,該節點指示別名的名稱以及有關片段類型的其他資訊。

Relay 編譯器也會在擴展中插入額外的欄位,使其能夠判斷片段是否已符合

fragment Foo on Node {
... on Viewer {
isViewer: __typename # <-- Relay inserts this
name
}
}

Relay 現在可以檢查回應中是否存在 isViewer 欄位,以知道片段是否已符合。

當 Relay 使用其執行階段成品從儲存區讀取片段內容時,它會使用此資訊將片段索引鍵附加到此新欄位,而不是直接附加到父物件。

雖然 @alias 是 Relay 特有的功能,但它的靈感來自於 GraphQL RFC Fragment Modularity 中概述的片段模組化。