跳到主要內容
版本: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.dev.org.tw/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.dev.org.tw/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.dev.org.tw/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.dev.org.tw/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: 擷取失敗,因為 connection禁止的標頭名稱

問:為什麼我仍然在網路標籤中看到發出的網路請求?

預設情況下,@tanstack/react-query(我們用於資料擷取掛鉤)會在掛載和視窗重新聚焦時重新擷取資料,即使它已經透過 SSR 取得初始資料。這可確保資料始終是最新的。如果你想停用此行為,請參閱 SSG 頁面。