借助 Firebase Data Connect 客户端 SDK,您可以直接从 Firebase 应用调用服务器端查询和突变。在设计部署到 Data Connect 服务的架构、查询和突变时,您可以并行生成自定义客户端 SDK。然后,将此 SDK 中的方法集成到您的客户端逻辑中。
正如我们在其他地方提到的,请务必注意,Data Connect查询和变异不是由客户端代码提交的,而是在服务器上执行的。相反,部署后,Data Connect 操作会像 Cloud Functions 一样存储在服务器上。这意味着您需要部署相应的客户端更改,以避免影响现有用户(例如,使用旧版应用的用户)。
因此,Data Connect 为您提供了一个开发者环境和工具,让您可以对服务器部署的架构、查询和突变进行原型设计。它还会在您进行原型设计时自动生成客户端 SDK。
当您对服务和客户端应用进行迭代更新后,服务器端和客户端更新即可部署。
客户端开发工作流程是怎样的?
如果您按照使用入门中的说明操作,则已了解 Data Connect 的整体开发流程。在本指南中,您可以详细了解如何从架构生成 Web SDK,以及如何处理客户端查询和变更。
总而言之,如需在客户端应用中使用生成的 Web SDK,您需要先完成以下前提步骤:
- 将 Firebase 添加到您的网页应用。
然后:
- 开发应用架构。
- 使用 JavaScript SDK 或 React/Angular 库初始化客户端代码。
- 对于 React 和 Angular,安装 Tanstack Query 软件包
设置 SDK 生成:
- 借助 Data Connect VS Code 扩展程序中的将 SDK 添加到应用按钮
- 通过更新 JavaScript SDK、React 或 Angular 的
connector.yaml
。
使用 JavaScript SDK、React 或 Angular 导入库和生成的代码。
使用 JavaScript SDK 或 React 或 Angular 实现对查询和突变的调用。
通过使用 JavaScript SDK 或 React/Angular 设置 Data Connect 模拟器进行测试。
使用 Firebase JavaScript SDK 实现客户端代码
本部分介绍了如何使用 Firebase JavaScript SDK 实现客户端。
如果您使用的是 React 或 Angular,请参阅有关为框架生成 Data Connect SDK 的替代设置说明和指向其他文档的链接。
初始化您的应用
首先,使用标准 Firebase 序列初始化您的应用。
initializeApp({...});
生成 JavaScript SDK
与大多数 Firebase 项目一样,Firebase Data Connect客户端Firebase Data Connect代码的开发工作在本地项目目录中进行。Data Connect VS Code 扩展程序和 Firebase CLI 都是用于生成和管理客户端代码的重要本地工具。
SDK 生成选项与您初始化项目时生成的 dataconnect.yaml
文件中的多个条目相关联。
初始化 SDK 生成
在connector.yaml
中,添加 outputDir
、package
和(对于 Web SDK)packageJsonDir
。
generate:
javascriptSdk:
outputDir: "../movies-generated"
package: "@movie-app/movies"
packageJsonDir: "../../"
outputDir
用于指定生成的 SDK 应输出到何处。
package
指定软件包名称。
packageJsonDir
指定软件包的安装位置。
在这种情况下,请安装 firebase@latest
以确保满足此对等依赖项。
初始化 JavaScript SDK
使用您在设置 Data Connect 时所用的信息(全部可在 Firebase 控制台的“Data Connect”标签页中找到)初始化 Data Connect 实例。
ConnectorConfig 对象
SDK 需要连接器配置对象。
此对象是根据 dataconnect.yaml
中的 serviceId
和 location
以及 connector.yaml
中的 connectorId
自动生成的。
导入库
初始化客户端代码需要两组导入:常规 Data Connect 导入和特定的已生成 SDK 导入。
请注意,常规导入中包含 ConnectorConfig
对象。
// general imports
import { ConnectorConfig, DataConnect, getDataConnect, QueryRef, MutationRef, QueryPromise, MutationPromise } from 'firebase/data-connect';
// generated queries and mutations from SDK
import { listMovies, ListMoviesResponse, createMovie, connectorConfig } from '@myorg/myconnector';
使用 JavaScript SDK 中的查询
生成的代码将已包含预定义的查询引用。您只需导入这些模型并对其调用 execute。
import { executeQuery } from 'firebase/data-connect';
import { listMoviesRef } from '@movie-app/movies';
const ref = listMoviesRef();
const { data } = await executeQuery(ref);
console.log(data.movies);
调用 SDK 查询方法
以下示例使用了这些操作快捷方式函数:
import { listMovies } from '@movie-app/movies';
function onBtnClick() {
// This will call the generated JS from the CLI and then make an HTTP request out
// to the server.
listMovies().then(data => showInUI(data)); // == executeQuery(listMoviesRef);
}
订阅更改
您可以订阅更改(这样,每次执行查询时都会进行更新)。
const listRef = listAllMoviesRef();
// subscribe will immediately invoke the query if no execute was called on it previously.
subscribe(listRef, ({ data }) => {
updateUIWithMovies(data.movies);
});
await createMovie({ title: 'Empire Strikes Back', releaseYear: 1980, genre: "Sci-Fi", rating: 5 });\
await listMovies(); // will update the subscription above`
处理对枚举字段的更改
应用的架构可以包含枚举,这些枚举可供 GraphQL 查询访问。
随着应用设计发生变化,您可能会添加新的枚举支持值。例如,假设在应用生命周期的后期,您决定向 AspectRatio
枚举添加 FULLSCREEN 值。
在 Data Connect 工作流中,您可以使用本地开发工具来更新查询和 SDK。
不过,在您发布客户端的更新版本之前,已部署的旧版客户端可能会出现故障。
弹性实现示例
始终为枚举值的 switch
语句添加 default
分支,或为与枚举值进行比较的 if/else if
块添加 else
分支。JavaScript/TypeScript 语言不会强制执行此操作,但这是在添加新的枚举值时使客户端代码保持稳健的方法。
const queryResult = await getOldestMovie();
if (queryResult.data) {
// we can use a switch statement's "default" case to check for unexpected values
const oldestMovieAspectRatio = queryResult.data.originalAspectRatio;
switch (oldestMovieAspectRatio) {
case AspectRatio.ACADEMY:
case AspectRatio.WIDESCREEN:
case AspectRatio.ANAMORPHIC:
console.log('This movie was filmed in Academy, widescreen or anamorphic aspect ratio!');
break;
default:
// the default case will catch FULLSCREEN, UNAVAILABLE or _UNKNOWN
// it will also catch unexpected values the SDK isn't aware of, such as CINEMASCOPE
console.log('This movie was was NOT filmed in Academy, widescreen or anamorphic.');
break;
}
// alternatively, we can check to see if the returned enum value is a known value
if (!Object.values(AspectRatio).includes(oldestMovieAspectRatio)) {
console.log(`Unrecognized aspect ratio: ${oldestAspectRatio}`);
}
} else {
console.log("no movies found!");
}
使用 JavaScript SDK 中的突变
突变的访问方式与查询相同。
import { executeMutation } from 'firebase/data-connect';
import { createMovieRef } from '@movie-app/movies';
const { data } = await executeMutation(createMovieRef({ movie: 'Empire Strikes Back' }));
连接到 Data Connect 模拟器
(可选)您可以通过调用 connectDataConnectEmulator
并传入 Data Connect 实例来连接到模拟器,如下所示:
import { connectDataConnectEmulator } from 'firebase/data-connect';
import { connectorConfig } from '@myorg/myconnector'; // Replace with your package name
const dataConnect = getDataConnect(connectorConfig);
connectDataConnectEmulator(dataConnect, 'localhost', 9399);`
// Make calls from your app
如需切换到生产资源,请注释掉用于连接到模拟器的行。
为 React 和 Angular 实现客户端代码
Firebase Data Connect 提供了一个生成的 SDK,其中包含使用 Invertase 合作伙伴提供的库(即 TanStack Query Firebase)的 React 和 Angular 钩子。
此库提供了一组钩子,可大大简化在应用中使用 Firebase 处理异步任务的操作。
初始化您的应用
首先,与任何 Firebase Web 应用一样,使用标准 Firebase 序列初始化您的应用。
initializeApp({...});
安装 TanStack Query Firebase 软件包
在项目中安装 TanStack Query 的软件包。
回应
npm i --save @tanstack/react-query @tanstack-query-firebase/react
npm i --save firebase@latest # Note: React has a peer dependency on ^11.3.0
Angular
ng add @angular/fire
生成 React 或 Angular SDK
与标准 Web SDK 一样(如前文所述),Firebase 工具会根据您的架构和操作自动生成 SDK。
如需为项目生成 React SDK,请向 connector.yaml
配置文件添加 react
键。
回应
generate:
javascriptSdk:
react: true
outputDir: "../movies-generated"
package: "@movie-app/movies"
packageJsonDir: "../../"
Angular
generate:
javascriptSdk:
angular: true
outputDir: "../movies-generated"
package: "@movie-app/movies"
packageJsonDir: "../../"
导入库
您需要四组导入才能初始化 React 或 Angular 客户端代码:常规 Data Connect 导入、常规 TanStack 导入,以及针对 JS 和 React 生成的 SDK 的特定导入。
请注意,常规导入中包含 ConnectorConfig
类型。
回应
// general imports
import { ConnectorConfig, DataConnect, getDataConnect, QueryRef, MutationRef, QueryPromise, MutationPromise } from 'firebase/data-connect';
// TanStack Query-related functions
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// generated queries and mutations from SDK
import { ListMoviesResponse, connectorConfig } from '@myorg/myconnector';
// generated React hooks from SDK
import { useListAllMovies, useCreateMovie } from "@myorg/connector/react";
Angular
// general imports
import { ConnectorConfig, DataConnect, getDataConnect, QueryRef, MutationRef, QueryPromise, MutationPromise } from 'firebase/data-connect';
// TanStack Query-related functions
import { provideTanStackQuery, QueryClient } from "@tanstack/angular-query-experimental";
// generated queries and mutations from SDK
import { ListMoviesResponse, connectorConfig } from '@myorg/myconnector';
// generated React hooks from SDK
import { injectListAllMovies, injectCreateMovie } from "@myorg/connector/angular";
在 React 或 Angular 客户端中使用查询和 mutation
设置完成后,您就可以使用生成的 SDK 中的方法了。
在以下代码段中,请注意以 use
为前缀的 React 方法 useListAllMovies
和以 inject
为前缀的 Angular 方法 injectListAllMovies
,这两个方法均来自生成的 SDK。
回应
生成的 SDK 中的所有此类操作(包括查询和变异)都会调用 TanStackQuery 绑定:
- 查询调用并返回 TanStack
useDataConnectQuery
hook - mutation 调用并返回 TanStack
useDataConnectMutation
hook
import { useListAllMovies } from '@movies-app/movies/react';
function MyComponent() {
const { isLoading, data, error } = useListAllMovies();
if(isLoading) {
return <div>Loading...</div>
}
if(error) {
return <div> An Error Occurred: {error} </div>
}
}
// App.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import MyComponent from './my-component';
function App() {
const queryClient = new QueryClient();
return <QueryClientProvider client={queryClient}>
<MyComponent />
</QueryClientProvider>
}
Angular
import { injectAllMovies, connectorConfig } from '@movies-app/movies/angular';
import { provideDataConnect, getDataConnect } from '@angular/fire/data-connect';
import { provideTanStackQuery, QueryClient } from "@tanstack/angular-query-experimental";
const queryClient = new QueryClient();
...
providers: [
...
provideTanStackQuery(queryClient),
provideDataConnect(() => {
const dc = getDataConnect(connectorConfig);
return dc;
})
]
在 React 和 Angular 中使用自动重新加载查询
您可以将查询配置为在数据发生变化时自动重新加载。
回应
export class MovieListComponent {
movies = useListAllMovies();
}
export class AddPostComponent {
const mutation = useCreateMovie({ invalidate: [listAllMoviesRef()] });
addMovie() {
// The following will automatically cause Tanstack to reload its listAllMovies query
mutation.mutate({ title: 'The Matrix });
}
}
Angular
// class
export class MovieListComponent {
movies = injectListAllMovies();
}
// template
@if (movies.isPending()) {
Loading...
}
@if (movies.error()) {
An error has occurred: {{ movies.error() }}
}
@if (movies.data(); as data) {
@for (movie of data.movies; track movie.id) {
<mat-card appearance="outlined">
<mat-card-content>{{movie.description}}</mat-card-content>
</mat-card>
} @empty {
<h2>No items!</h2>
}
}
连接到 Data Connect 模拟器
(可选)您可以通过调用 connectDataConnectEmulator
连接到模拟器,然后将 Data Connect 实例传递给生成的钩子,如下所示:
React
import { getDataConnect, connectDataConnectEmulator } from 'firebase/data-connect';
import { connectorConfig } from '@movies-app/movies';
import { useListAllMovies } from '@movies-app/movies/react';
const dc = getDataConnect(connectorConfig);
connectDataConnectEmulator(dc, 'localhost', 9399);
class AppComponent() {
...
const { isLoading, data, error } = useListAllMovies(dc);
...
}
Angular
// app.config.ts
import { provideDataConnect } from '@angular/fire/data-connect';
import { getDataConnect, connectDataConnectEmulator } from 'firebase/data-connect';
provideDataConnect(() => {
const dc = getDataConnect(connectorConfig);
connectDataConnectEmulator(dc, 'localhost', 9399);
return dc;
}),
如需切换到生产资源,请注释掉用于连接到模拟器的行。
SDK 中的数据类型
Data Connect 服务器表示常见的 GraphQL 数据类型。在 SDK 中,这些参数表示如下。
数据连接类型 | TypeScript |
---|---|
时间戳 | 字符串 |
日期 | 字符串 |
UUID | 字符串 |
Int64 | 字符串 |
双精度型 | 数字 |
浮点数 | 数字 |
SDK 生成的特殊注意事项
配置相对于 node_modules
的路径
对于 JavaScript SDK,由于 Data Connect 使用 npm link
安装您的 SDK,因此生成的 SDK 需要输出到与 node_modules
路径处于同一级别的目录或可访问 node_modules
的子目录中。
换句话说,生成的 SDK 需要有权访问 firebase
节点模块才能正常运行。
例如,如果您的 node_modules
位于 my-app/
中,则输出目录应为 my-app/js-email-generated
,以便 js-email-generated
可以从其父级 node_modules
文件夹导入。
my-app/
dataconnect/
connector/
connector.yaml
node_modules/
firebase/
js-email-generated/
// connector.yaml
connectorId: "my-connector"
generate:
javascriptSdk:
outputDir: "../../js-email-generated"
package: "@myapp/my-connector"
或者,如果您有一个模块托管在根目录中的单体代码库,则可以将输出目录放在单体代码库中的任何文件夹中。
my-monorepo/
dataconnect/
connector/
connector.yaml
node_modules/
firebase/
my-app/
js-email-generated/
package.json
// connector.yaml
connectorId: "my-connector"
generate:
javascriptSdk:
outputDir: "../../my-app/js-email-generated" # You can also output to ../../js-email-generated
在原型设计期间更新 SDK
如果您使用 Data Connect VS Code 扩展程序及其 Data Connect 模拟器进行交互式原型设计,那么在您修改定义架构、查询和变更的 .gql
文件时,系统会自动生成并更新 SDK 源文件。在热(重新)加载工作流中,这可能是一项实用功能。
.gql
更新,还可以自动更新 SDK 来源。
或者,您也可以使用 CLI 在每次更改 .gql 文件时重新生成 SDK:
firebase dataconnect:sdk:generate --watch
生成用于集成和正式版发布的 SDK
在某些情况下(例如准备要提交以进行 CI 测试的项目源),您可以调用 Firebase CLI 进行批量更新。
在这些情况下,请使用 firebase dataconnect:sdk:generate
。