跳至主要内容
版本:11.x

服务器端渲染

要启用 SSR,只需在您的 createTRPCNext 配置回调中设置 ssr: true

信息

当您启用 SSR 时,tRPC 将使用 getInitialProps 在服务器上预取所有查询。这会导致问题 像这样 当您使用 getServerSideProps 时,解决它不在我们控制范围内。

 
或者,您可以保持 SSR 禁用(默认值)并使用 服务器端助手getStaticPropsgetServerSideProps 中预取查询。

为了在服务器端渲染步骤中正确执行查询,我们需要在我们的 config 中添加额外的逻辑

此外,请考虑 响应缓存.

utils/trpc.ts
tsx
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import { ssrPrepass } from '@trpc/next/ssrPrepass';
import superjson from 'superjson';
import type { AppRouter } from './api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
ssr: true,
ssrPrepass,
config(opts) {
const { ctx } = opts;
if (typeof window !== 'undefined') {
// during client requests
return {
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
links: [
httpBatchLink({
// The server needs to know your app's full url
url: `${getBaseUrl()}/api/trpc`,
/**
* Set custom request headers on every request from tRPC
* @link https://trpc.node.org.cn/docs/v10/header
*/
headers() {
if (!ctx?.req?.headers) {
return {};
}
// To use SSR properly, you need to forward client headers to the server
// This is so you can pass through things like cookies when we're server-side rendering
return {
cookie: ctx.req.headers.cookie,
};
},
}),
],
};
},
});
utils/trpc.ts
tsx
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import { ssrPrepass } from '@trpc/next/ssrPrepass';
import superjson from 'superjson';
import type { AppRouter } from './api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
ssr: true,
ssrPrepass,
config(opts) {
const { ctx } = opts;
if (typeof window !== 'undefined') {
// during client requests
return {
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
links: [
httpBatchLink({
// The server needs to know your app's full url
url: `${getBaseUrl()}/api/trpc`,
/**
* Set custom request headers on every request from tRPC
* @link https://trpc.node.org.cn/docs/v10/header
*/
headers() {
if (!ctx?.req?.headers) {
return {};
}
// To use SSR properly, you need to forward client headers to the server
// This is so you can pass through things like cookies when we're server-side rendering
return {
cookie: ctx.req.headers.cookie,
};
},
}),
],
};
},
});

或者,如果您想根据给定请求有条件地进行 SSR,您可以将回调传递给 ssr。此回调可以返回一个布尔值,或一个解析为布尔值的 Promise

utils/trpc.ts
tsx
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';
import type { AppRouter } from './api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
config(opts) {
const { ctx } = opts;
if (typeof window !== 'undefined') {
// during client requests
return {
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
links: [
httpBatchLink({
// The server needs to know your app's full url
url: `${getBaseUrl()}/api/trpc`,
/**
* Set custom request headers on every request from tRPC
* @link https://trpc.node.org.cn/docs/v10/header
*/
headers() {
if (!ctx?.req?.headers) {
return {};
}
// To use SSR properly, you need to forward client headers to the server
// This is so you can pass through things like cookies when we're server-side rendering
return {
cookie: ctx.req.headers.cookie,
};
},
}),
],
};
},
ssr(opts) {
// only SSR if the request is coming from a bot
return opts.ctx?.req?.headers['user-agent']?.includes('bot');
},
});
utils/trpc.ts
tsx
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';
import type { AppRouter } from './api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
config(opts) {
const { ctx } = opts;
if (typeof window !== 'undefined') {
// during client requests
return {
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
links: [
httpBatchLink({
// The server needs to know your app's full url
url: `${getBaseUrl()}/api/trpc`,
/**
* Set custom request headers on every request from tRPC
* @link https://trpc.node.org.cn/docs/v10/header
*/
headers() {
if (!ctx?.req?.headers) {
return {};
}
// To use SSR properly, you need to forward client headers to the server
// This is so you can pass through things like cookies when we're server-side rendering
return {
cookie: ctx.req.headers.cookie,
};
},
}),
],
};
},
ssr(opts) {
// only SSR if the request is coming from a bot
return opts.ctx?.req?.headers['user-agent']?.includes('bot');
},
});
pages/_app.tsx
tsx
import { trpc } from '~/utils/trpc';
import type { AppProps } from 'next/app';
import React from 'react';
const MyApp: AppType = ({ Component, pageProps }: AppProps) => {
return <Component {...pageProps} />;
};
export default trpc.withTRPC(MyApp);
pages/_app.tsx
tsx
import { trpc } from '~/utils/trpc';
import type { AppProps } from 'next/app';
import React from 'react';
const MyApp: AppType = ({ Component, pageProps }: AppProps) => {
return <Component {...pageProps} />;
};
export default trpc.withTRPC(MyApp);

常见问题解答

问:为什么我需要手动将客户端的标头转发到服务器?为什么 tRPC 不自动为我执行此操作?

虽然在进行 SSR 时您可能不希望将客户端的标头转发到服务器,但您可能希望在标头中动态添加内容。因此,tRPC 不想承担标头键冲突等的责任。

问:为什么在 Node 18 上使用 SSR 时我需要删除 connection 标头?

如果您不删除 connection 标头,数据获取将失败并出现 TRPCClientError: fetch failed,因为 connection 是一个 禁止的标头名称.

问:为什么我仍然看到网络选项卡中正在进行网络请求?

默认情况下,@tanstack/react-query(我们用于数据获取钩子的工具)会在挂载和窗口重新聚焦时重新获取数据,即使它已经通过 SSR 获取了初始数据。这确保数据始终是最新的。如果您想禁用此行为,请查看关于 SSG 的页面。