Skip to main content
Version: 10.x

服务器端渲染

要启用 SSR,只需在 createTRPCNext 配置回调中设置 ssr: true 即可。

¥To enable SSR just set ssr: true in your createTRPCNext config callback.

信息

当你启用 SSR 时,tRPC 将使用 getInitialProps 来预取服务器上的所有查询。当你使用 getServerSideProps 时,这会导致问题 像这样,而解决它是我们无法解决的。

¥When you enable SSR, tRPC will use getInitialProps to prefetch all queries on the server. This results in problems like this when you use getServerSideProps, and solving it is out of our hands.

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

¥ \ Alternatively, you can leave SSR disabled (the default) and use Server-Side Helpers to prefetch queries in getStaticProps or getServerSideProps.

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

¥In order to execute queries properly during the server-side render step we need to add extra logic inside our config:

此外,请考虑 Response Caching

¥Additionally, consider Response Caching.

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 {
transformer: superjson, // optional - adds superjson serialization
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
transformer: superjson, // optional - adds superjson serialization
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.nodejs.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: true,
});
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 {
transformer: superjson, // optional - adds superjson serialization
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
transformer: superjson, // optional - adds superjson serialization
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.nodejs.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: true,
});

或者,如果你想根据给定请求进行 SSR,你可以将回调传递给 ssr。此回调可以返回布尔值,或解析为布尔值的 Promise:

¥or, if you want to SSR conditional on a given request, you can pass a callback to ssr. This callback can return a boolean, or a Promise resolving to a boolean:

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 {
transformer: superjson, // optional - adds superjson serialization
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
transformer: superjson, // optional - adds superjson serialization
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.nodejs.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 {
transformer: superjson, // optional - adds superjson serialization
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
};
}
return {
transformer: superjson, // optional - adds superjson serialization
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.nodejs.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);

常见问题

¥FAQ

问:为什么我需要手动将客户端的标头转发到服务器?为什么 tRPC 不自动为我做这件事?

¥Q: Why do I need to forward the client's headers to the server manually? Why doesn't tRPC automatically do that for me?

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

¥While it's rare that you wouldn't want to forward the client's headers to the server when doing SSR, you might want to add things dynamically in the headers. Therefore, tRPC doesn't want to take responsibility for header keys colliding, etc.

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

¥Q: Why do I need to delete the connection header when using SSR on Node 18?

如果不删除 connection 标头,则 TRPCClientError: fetch failed 的数据获取将会失败,因为 connection禁止的标头名称

¥If you don't remove the connection header, the data fetching will fail with TRPCClientError: fetch failed because connection is a forbidden header name.

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

¥Q: Why do I still see network requests being made in the Network tab?

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

¥By default, @tanstack/react-query (which we use for the data fetching hooks) refetches data on mount and window refocus, even if it's already got initial data via SSR. This ensures data is always up-to-date. See the page on SSG if you'd like to disable this behavior.