Next.js - 页脚/页眉仅通过 API 加载一次,服务器端
Next.js - Footer / Header loading via API only once, server side
我正在构建一个 Next.js 无头应用程序,我通过 API 调用 Umbraco 后端获取数据。我正在使用 getServerSideProps 为我的每个页面加载数据,然后将其作为“数据”传递到功能组件和页面中。
我遇到的问题是网站的页眉/页脚部分有单独的端点,并且在所有页面之间共享。因此,每页调用 3 次(页眉、数据、页脚)是一种耻辱和糟糕的做法。
为了获取页眉/页脚一次,然后将其保存在多个页面上,同时保持 SSR,可以做些什么? (重要)。我试过使用 cookie,但它们不能保存那么多数据。下面是一些代码:
页面正在获取数据:
export async function getServerSideProps({ locale }) {
const footer = await Fetcher(footerEndPoint, locale);
return {
props: {
locale: locale,
footer: footer
}
}
}
布局
const Layout = (props) => {
const { children, footer } = props;
return (
<>
<Header />
<main>
{children}
</main>
<Footer footer={footer} />
</>
);
};
export default Layout;
我看到三个选项可以实现仅 SSR 数据获取一次,以获取在页面转换之间永远不会改变的内容:
1。 _app.ts
中的 getInitialProps()
您可以在 _app.tsx 中使用 getInitialProps()
。这首先在服务器上运行,您可以将响应值缓存在变量中。下次执行 getInitialProps()
时,它将只提供缓存的值,而不是触发另一个请求。要使此工作在客户端进行,您必须在 useEffect
:
中重新水化缓存变量
// pages/_app.tsx
let navigationPropsCache
function MyApp({ Component, pageProps, navigationProps }) {
useEffect(
()=>{
navigationPropsCache = navigationProps
},
[]
)
return <>
<Navigation items={navigationProps}/>
<Component {...pageProps} />
</>
}
MyApp.getInitialProps = async () => {
if(navigationPropsCache) {
return {navigationProps: navigationPropsCache}
}
const res = await fetch("http://localhost:3000/api/navigation")
const navigationProps = await res.json()
navigationPropsCache = navigationProps
return {navigationProps}
}
请注意,getInitialProps()
是自下一个 9.3 以来已弃用的功能。不确定将来会支持多长时间。参见:https://nextjs.org/docs/api-reference/data-fetching/getInitialProps
有关完整代码示例,请参阅 https://github.com/breytex/firat500/tree/trivial-getInitialProps。
2。使用自定义下一个服务器实现
此解决方案基于两个想法:
- 使用自定义 server.ts 拦截 nextjs SSR 功能。获取您需要的所有数据,在服务器端呈现导航栏和页脚,将组件 HTML 注入 SSR 结果。
- 根据您还作为
<script>
. 附加到 DOM 的已提取数据的字符串化版本对 DOM 进行补水
// server/index.ts
server.all("*", async (req, res) => {
const html = await app.renderToHTML(req, res, req.path, req.query);
const navigationProps = await getNavigationProps()
const navigationHtml = renderToString(React.createElement(Navigation, {items: navigationProps}))
const finalHtml = html
.replace("</body>", `<script>window.navigationProps = ${JSON.stringify(navigationProps)};</script></body>`)
.replace("{{navigation-placeholder}}", navigationHtml)
return res.send(finalHtml);
});
// components/Navigation.tsx
export const Navigation: React.FC<Props> = ({items})=>{
const [finalItems, setFinalItems] = useState(items ?? [])
useEffect(
()=>{
setFinalItems((window as any).navigationProps)
},
[]
)
if(!Array.isArray(finalItems) || finalItems.length === 0) return <div>{"{{navigation-placeholder}}"}</div>
return (
<div style={{display:"flex", maxWidth: "500px", justifyContent: "space-between", marginTop: "100px"}}>
{finalItems.map(item => <NavigationItem {...item}/>)}
</div>
)
}
目前我认为这是一个非常肮脏的例子,但您可以基于此构建一些强大的东西。
在此处查看完整代码:https://github.com/breytex/firat500/tree/next-link-navigation
3。使用 react-ssr-prepass 执行所有数据获取服务器端
- 这使用具有某种缓存的定制提取包装器
- 服务器端遍历React组件树,执行所有的数据抓取功能。这会填充缓存。
- 缓存的状态被发送到客户端并重新水化客户端缓存
- 在 DOM 重新水合时,所有数据都从该缓存中提供,因此不会第二次发送请求
这个例子有点长,它基于 urql
项目的杰出工作:https://github.com/FormidableLabs/next-urql/blob/master/src/with-urql-client.tsx
在此处查看完整示例:https://github.com/breytex/firat500/tree/prepass
结论:
只要可行,我个人会选择选项 #1。
#3 看起来是一种具有良好开发人员体验的方法,适合更大的团队。 #2 需要一些爱才能真正有用 :D
我正在构建一个 Next.js 无头应用程序,我通过 API 调用 Umbraco 后端获取数据。我正在使用 getServerSideProps 为我的每个页面加载数据,然后将其作为“数据”传递到功能组件和页面中。
我遇到的问题是网站的页眉/页脚部分有单独的端点,并且在所有页面之间共享。因此,每页调用 3 次(页眉、数据、页脚)是一种耻辱和糟糕的做法。
为了获取页眉/页脚一次,然后将其保存在多个页面上,同时保持 SSR,可以做些什么? (重要)。我试过使用 cookie,但它们不能保存那么多数据。下面是一些代码:
页面正在获取数据:
export async function getServerSideProps({ locale }) {
const footer = await Fetcher(footerEndPoint, locale);
return {
props: {
locale: locale,
footer: footer
}
}
}
布局
const Layout = (props) => {
const { children, footer } = props;
return (
<>
<Header />
<main>
{children}
</main>
<Footer footer={footer} />
</>
);
};
export default Layout;
我看到三个选项可以实现仅 SSR 数据获取一次,以获取在页面转换之间永远不会改变的内容:
1。 _app.ts
中的 getInitialProps()您可以在 _app.tsx 中使用 getInitialProps()
。这首先在服务器上运行,您可以将响应值缓存在变量中。下次执行 getInitialProps()
时,它将只提供缓存的值,而不是触发另一个请求。要使此工作在客户端进行,您必须在 useEffect
:
// pages/_app.tsx
let navigationPropsCache
function MyApp({ Component, pageProps, navigationProps }) {
useEffect(
()=>{
navigationPropsCache = navigationProps
},
[]
)
return <>
<Navigation items={navigationProps}/>
<Component {...pageProps} />
</>
}
MyApp.getInitialProps = async () => {
if(navigationPropsCache) {
return {navigationProps: navigationPropsCache}
}
const res = await fetch("http://localhost:3000/api/navigation")
const navigationProps = await res.json()
navigationPropsCache = navigationProps
return {navigationProps}
}
请注意,getInitialProps()
是自下一个 9.3 以来已弃用的功能。不确定将来会支持多长时间。参见:https://nextjs.org/docs/api-reference/data-fetching/getInitialProps
有关完整代码示例,请参阅 https://github.com/breytex/firat500/tree/trivial-getInitialProps。
2。使用自定义下一个服务器实现
此解决方案基于两个想法:
- 使用自定义 server.ts 拦截 nextjs SSR 功能。获取您需要的所有数据,在服务器端呈现导航栏和页脚,将组件 HTML 注入 SSR 结果。
- 根据您还作为
<script>
. 附加到 DOM 的已提取数据的字符串化版本对 DOM 进行补水
// server/index.ts
server.all("*", async (req, res) => {
const html = await app.renderToHTML(req, res, req.path, req.query);
const navigationProps = await getNavigationProps()
const navigationHtml = renderToString(React.createElement(Navigation, {items: navigationProps}))
const finalHtml = html
.replace("</body>", `<script>window.navigationProps = ${JSON.stringify(navigationProps)};</script></body>`)
.replace("{{navigation-placeholder}}", navigationHtml)
return res.send(finalHtml);
});
// components/Navigation.tsx
export const Navigation: React.FC<Props> = ({items})=>{
const [finalItems, setFinalItems] = useState(items ?? [])
useEffect(
()=>{
setFinalItems((window as any).navigationProps)
},
[]
)
if(!Array.isArray(finalItems) || finalItems.length === 0) return <div>{"{{navigation-placeholder}}"}</div>
return (
<div style={{display:"flex", maxWidth: "500px", justifyContent: "space-between", marginTop: "100px"}}>
{finalItems.map(item => <NavigationItem {...item}/>)}
</div>
)
}
目前我认为这是一个非常肮脏的例子,但您可以基于此构建一些强大的东西。
在此处查看完整代码:https://github.com/breytex/firat500/tree/next-link-navigation
3。使用 react-ssr-prepass 执行所有数据获取服务器端
- 这使用具有某种缓存的定制提取包装器
- 服务器端遍历React组件树,执行所有的数据抓取功能。这会填充缓存。
- 缓存的状态被发送到客户端并重新水化客户端缓存
- 在 DOM 重新水合时,所有数据都从该缓存中提供,因此不会第二次发送请求
这个例子有点长,它基于 urql
项目的杰出工作:https://github.com/FormidableLabs/next-urql/blob/master/src/with-urql-client.tsx
在此处查看完整示例:https://github.com/breytex/firat500/tree/prepass
结论:
只要可行,我个人会选择选项 #1。 #3 看起来是一种具有良好开发人员体验的方法,适合更大的团队。 #2 需要一些爱才能真正有用 :D