类型发射
作为其正常工作的一部分,Relay 编译器 将为您的选择语言发射类型信息,帮助您编写类型安全的应用程序代码。这些类型包含在 relay-compiler
生成的用于描述您的操作和片段的工件中。
操作变量
用于查询、变异或订阅操作的变量对象的形状。
在这个例子中,发射的类型信息将要求变量对象包含一个 artistID
键,它是一个非空字符串。
- 流程
- TypeScript
/**
* export type ExampleQuery$variables = {
* +artistID: string,
* }
* export type ExampleQuery$data = {
* +artist: {
* +name: ?string,
* }
* }
* export type ExampleQuery = {
* +variables: ExampleQuery$variables,
* +response: ExampleQuery$data,
* }
*/
const data = useLazyLoadQuery(
graphql`
query ExampleQuery($artistID: ID!) {
artist(id: $artistID) {
name
}
}
`,
// variables are expected to be of type ExampleQuery$variables
{artistID: 'banksy'},
);
/**
* export type ExampleQuery$variables = {
* readonly artistID: string
* }
* export type ExampleQuery$data = {
* readonly artist?: {
* readonly name?: string
* }
* }
* export type ExampleQuery = {
* readonly variables: ExampleQuery$variables
* readonly response: ExampleQuery$data
* }
*/
const data = useLazyLoadQuery(
graphql`
query ExampleQuery($artistID: ID!) {
artist(id: $artistID) {
name
}
}
`,
// variables are expected to be of type ExampleQuery$variables
{artistID: 'banksy'},
);
操作和片段数据
操作或片段中选择的数据形状,遵循数据掩码规则。也就是说,排除片段扩展选择的所有数据。
在这个例子中,发射的类型信息描述了由 useLazyLoadQuery
(或 usePreloadedQuery
)返回的响应数据。
- 流程
- TypeScript
/**
* export type ExampleQuery$variables = {
* +artistID: string,
* }
* export type ExampleQuery$data = {
* +artist: {
* +name: ?string,
* }
* }
* export type ExampleQuery = {
* +variables: ExampleQuery$variables,
* +response: ExampleQuery$data,
* }
*/
// data is of type ExampleQuery$data
const data = useLazyLoadQuery(
graphql`
query ExampleQuery($artistID: ID!) {
artist(id: $artistID) {
name
}
}
`,
{artistID: 'banksy'},
);
return props.artist && <div>{props.artist.name} is great!</div>
/**
* export type ExampleQuery$variables = {
* readonly artistID: string
* }
* export type ExampleQuery$data = {
* readonly artist?: {
* readonly name?: string
* }
* }
* export type ExampleQuery = {
* readonly variables: ExampleQuery$variables
* readonly response: ExampleQuery$data
* }
*/
// data is of type ExampleQuery$data
const data = useLazyLoadQuery(
graphql`
query ExampleQuery($artistID: ID!) {
artist(id: $artistID) {
name
}
}
`,
{artistID: 'banksy'},
);
return props.artist && <div>{props.artist.name} is great!</div>
同样,在这个例子中,发射的类型信息描述了道具的类型,以匹配 useFragment
预计接收的片段引用的类型。
- 流程
- TypeScript
/**
* export type ExampleFragmentComponent_artist$data = {
* +name: string
* }
*
* export type ExampleFragmentComponent_artist$key = { ... }
*/
import type { ExampleFragmentComponent_artist$key } from "__generated__/ExampleFragmentComponent_artist.graphql"
type Props = {
artist: ExampleFragmentComponent_artist$key,
};
export default ExampleFragmentComponent(props) {
// data is of type ExampleFragmentComponent_artist$data
const data = useFragment(
graphql`
fragment ExampleFragmentComponent_artist on Artist {
biography
}
`,
props.artist,
);
return <div>About the artist: {props.artist.biography}</div>;
}
/**
* export type ExampleFragmentComponent_artist$data = {
* readonly name: string
* }
*
* export type ExampleFragmentComponent_artist$key = { ... }
*/
import { ExampleFragmentComponent_artist$key } from "__generated__/ExampleFragmentComponent_artist.graphql"
interface Props {
artist: ExampleFragmentComponent_artist$key,
};
export default ExampleFragmentComponent(props: Props) {
// data is of type ExampleFragmentComponent_artist$data
const data = useFragment(
graphql`
fragment ExampleFragmentComponent_artist on Artist {
biography
}
`,
props.artist,
);
return <div>About the artist: {props.artist.biography}</div>;
}
片段引用
在数据掩码中描述的不透明标识符,子容器期望从其父级接收,它代表子容器在其父级片段内的片段扩展。
考虑一个组合上面片段组件示例的组件。在这个例子中,子组件发射的类型信息接收一个唯一的非透明标识符类型,称为片段引用,它由父级片段引用在子级片段扩展的位置发射的类型信息引用。因此,确保子级的片段扩展到父级的片段中,并且在运行时将正确的片段引用传递给子组件。
- 流程
- TypeScript
import { ExampleFragmentComponent } from "./ExampleFragmentComponent"
/**
* import type { ExampleFragmentComponent_artist$fragmentType } from "ExampleFragmentComponent_artist.graphql";
*
* export type ExampleQuery$data = {
* +artist: ?{
* +name: ?string,
* +$fragmentSpreads: ExampleFragmentComponent_artist$fragmentType,
* }
* };
* export type ExampleQuery$variables = {
* +artistID: string,
* }
* export type ExampleQuery = {
* +variables: ExampleQuery$variables,
* +response: ExampleQuery$data,
* }
*/
// data is of type ExampleQuery$data
const data = useLazyLoadQuery(
graphql`
query ExampleQuery($artistID: ID!) {
artist(id: $artistID) {
name
...ExampleFragmentComponent_artist
}
}
`,
{artistID: 'banksy'},
);
// Here only `data.artist.name` is directly visible,
// the marker prop $fragmentSpreads indicates that `data.artist`
// can be used for the component expecting this fragment spread.
return <ExampleFragmentComponent artist={data.artist} />;
import { ExampleFragmentComponent } from "./ExampleFragmentComponent"
/**
* import { ExampleFragmentComponent_artist$fragmentType } from "ExampleFragmentComponent_artist.graphql";
*
* export type ExampleQuery$data = {
* readonly artist?: {
* readonly name: ?string,
* readonly " $fragmentSpreads": ExampleFragmentComponent_artist$fragmentType
* }
* }
* export type ExampleQuery$variables = {
* readonly artistID: string
* }
* export type ExampleQuery = {
* readonly variables: ExampleQuery$variables
* readonly response: ExampleQuery$data
* }
*/
// data is of type ExampleQuery$data
const data = useLazyLoadQuery(
graphql`
query ExampleQuery($artistID: ID!) {
artist(id: $artistID) {
name
...ExampleFragmentComponent_artist
}
}
`,
{artistID: 'banksy'},
);
// Here only `data.artist.name` is directly visible,
// the marker prop $fragmentSpreads indicates that `data.artist`
// can be used for the component expecting this fragment spread.
return <ExampleFragmentComponent artist={data.artist} />;
单个工件目录
需要注意的重要警告是,默认情况下,严格的片段引用类型信息不会发射,而是它们将被类型化为 any
,并允许您向子组件传递任何数据。
要启用此功能,您必须告诉编译器将所有工件存储在一个目录中,方法是在编译器配置中指定 artifactDirectory
{
// package.json
"relay": {
"artifactDirectory": "./src/__generated__",
...
},
...
}
…并在您的 .babelrc
配置中另外通知 babel 插件在何处查找工件
{
"plugins": [
["relay", { "artifactDirectory": "./src/__generated__" }]
]
}
建议在您的模块解析配置中为该目录创建别名,这样您就不需要在源文件中指定相对路径。这也是上面示例中所做的,其中工件是从 __generated__
别名导入,而不是像 ../../../../__generated__
这样的相对路径。
背景信息
原因是 relay-compiler
及其工件发射是无状态的。这意味着它不会跟踪原始源文件的 位置,以及编译器之前将随附的工件保存在磁盘上的位置。因此,如果编译器要发射尝试从其他工件导入片段引用类型的工件,编译器将
- 首先需要知道该其他工件在磁盘上的位置;
- 以及当该其他工件在磁盘上更改位置时更新导入。
Facebook 使用一个名为Haste的模块系统,其中所有源文件都被视为处于扁平命名空间中。这意味着导入声明不需要指定另一个模块的路径,因此编译器不需要考虑上述问题。即导入只需要指定模块文件名 的基本名称,Haste 在导入时负责实际找到正确的模块。然而,在 Facebook 之外,Haste 模块系统的使用是不存在的,也不鼓励,因此决定不导入片段引用类型,而是将它们类型化为 any
。
最简单地说,我们可以将 Haste 看作一个包含所有模块文件的单个目录,因此所有模块导入始终可以使用相对同级路径安全地导入。这就是单个工件目录功能所实现的。不是将工件与它们的源文件共置,而是将所有工件存储在一个目录中,允许编译器发射片段引用类型的导入。
此页面是否有用?
通过以下方式帮助我们使网站更完善 回答几个简单的问题.