跳至主要内容
版本: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 将片段键公开为它自己的可空属性,这使我们能够检查节点是否实际上实现了 Viewer,甚至允许 Flow 强制执行组件处理这种可能性!

@skip 和 @include

在片段上使用 @skip@include 指令时,也会出现类似的问题。为了安全地使用扩展片段,您需要检查它是否已获取。从历史上看,这需要访问用于确定片段是否被跳过或包含的查询变量。

使用 @alias,您现在可以通过简单地为片段分配别名,并检查别名是否为空来检查片段是否已获取

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} />
}

强制安全性

我们概述了在 Relay 中使用 @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。如果其中任何一个是空值,那么该空值将冒泡到 user.requiredFields 字段,使其成为空值。这使我们能够执行单一检查,而不会影响 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 片段模块化