如何在 Next.js 中禁用服务器端渲染 (SSR)
为什么要在 Next.js 中禁用 SSR?
如何在 Next.js 中禁用 SSR?
关于路由和其他高级功能的说明
了解如何在 Next.js 中禁用 SSR 并使用它来替换 Create React App 或任何自定义工具。
Next.js 是我最喜爱的 React 应用开发工具。它提供一流的开发体验。此外,它还拥有大量实用功能,可提高生产效率并优化您的应用:
- 静态和服务器渲染
- TypeScript 支持
- 多个入口点
- 捆绑拆分和摇树
然而,过去我并不想将它用于我所有的 React 应用。默认情况下,Next.js 在没有服务器端渲染 (SSR) 的情况下无法工作。当我的应用不需要 SSR 时,我更喜欢使用像 Create React App 这样的非 SSR 解决方案,因为 SSR 已经给我带来了很多不必要的问题。
然后,有一天,React Query 的作者 Tanner Linsley 在 Twitter 上发帖称,他正在使用不带 SSR 的 Next.js 来替代 Create React App:
我太兴奋了!研究了一番之后,我终于可以自己做了。🤯
为什么要在 Next.js 中禁用 SSR?
在某些情况下,React 应用程序的 SSR 是必要的:
- 应用程序的内容需要在搜索结果(SEO)中排名靠前。
- 您需要社交媒体预览(Facebook、Twitter、Slack 等)。
- 您需要它可以为您的用户提供的额外速度优化。
然而,使用 SSR 有多个权衡和挑战:
- 你需要一个复杂的托管环境。你不能直接将应用上传到服务器或 CDN。你需要 Node.js 服务器进行服务端渲染。这不仅增加了复杂性,也增加了成本。
- 你需要确保你的代码在浏览器和服务器(Node.js)上都能正常工作。这会使调试更加困难,并且在某些情况下会限制你的操作。例如,你不能使用 localStorage 来存储授权信息,而需要将其传入 cookie 中,并使用一个可以在服务器和浏览器上运行的 cookie 库。
- 它会影响你的应用程序架构。例如,服务器端渲染需要在单次渲染中完成,因此你需要在一个位置(例如
getInitialProps
)获取页面的所有数据。这一要求使得使用 Redux 或 React Query 等库获取数据变得复杂,并且经常导致代码重复。
如果您不需要 SSR,这些权衡就不值得。基本上,您应该考虑为所有需要登录才能使用 UI 的应用禁用 SSR。
启发式:如果您的应用程序需要登录,您可能应该禁用 SSR。
如何在 Next.js 中禁用 SSR?
npx create-next-app
让我们看一下为新的 Next.js 应用程序(使用 创建)禁用 SSR 的步骤。
步骤 1:将所有请求重写为pages/index.js
Next.js 支持添加重定向next.config.js
。在项目根目录中创建一个文件,并在其中添加以下配置:
module.exports = {
target: "serverless",
async rewrites() {
return [
// Rewrite everything to `pages/index`
{
source: "/:any*",
destination: "/",
},
];
},
};
这些重定向仅在开发环境中有效。在生产环境中,您需要拥有像 NGINX 这样的代理服务器,或者使用托管平台的功能(例如Netlify 的重定向)来执行这些重定向。
步骤 2:禁用页面内容的 SSR
要禁用页面内容的 SSR,我们需要添加以下代码pages/_app.js
:
import '../styles/globals.css'
function SafeHydrate({ children }) {
return (
<div suppressHydrationWarning>
{typeof window === 'undefined' ? null : children}
</div>
)
}
function MyApp({ Component, pageProps }) {
return <SafeHydrate><Component {...pageProps} /></SafeHydrate>
}
export default MyApp
在上面的代码中,我们将页面内容包装到一个名为 的组件中SafeHydrate
,这样就可以防止页面内容在服务器上渲染。让我们来看看上面的代码到底做了什么。
window
使用 Next.js,您可以通过检查对象是否来检查我们是否在服务器上undefined
。
if(typeof window === 'undefined') {
// This code will only execute on the server
// and not in the browser
}
但是,我们不能直接将代码包装到这个if
语句中。如果你尝试这样做,你会注意到 React 会在控制台中生成一个恼人的 hydration mismatch 警告:Warning: Expected server HTML to contain a matching <div> in <div>.
如果服务器 HTML 与浏览器渲染的 HTML 不同,就会发生这种情况。
在我们的例子中,忽略此警告是安全的。为了保持整洁,我们希望完全隐藏控制台中的警告。这可以通过使用 prop 渲染一个 div 来实现suppressHydrationWarning
。为了提高可读性,我们SafeHydrate
为此创建了一个单独的组件,并将页面组件包装在其中。
步骤 3:检查一切是否正常npm run dev
现在,npm run dev
在终端中运行。服务器在http://localhost:3000/运行后,你应该能够访问任何 URL(例如http://localhost:3000/some/random/path)并在那里看到 index.js 的内容。
成功!🎉
步骤 4:使用next export
我们希望将应用程序部署为静态包,以便无需 Node.js 服务器即可提供服务。为此,Next.js 提供了命令next export
。它将在目录中创建应用程序的静态版本out
。
要使用该命令,请像这样更新“build”脚本package.json
:
"scripts": {
...
"build": "next build && next export"
...
}
现在,运行npm run build
。当你看到消息 时Export successful
,恭喜!你现在在目录中有一个可以运行的静态 Next.js 应用out
。🎉
您可以从此Github 存储库查看整个示例应用程序
关于路由和其他高级功能的说明
路由
如果您没有运行服务器,Next.js 不支持动态路由。您需要一个类似 的路由器react-router
。设置方法与其他工具(例如 Create React App)相同。
更新<title>
和其他<head>
标签
您不需要添加类似的内容react-helmet
来更新head
,Next.js<Head />
组件就可以工作。
拥有多个独立页面
如果需要,您仍然可以使用 Next.js 页面,将多个不同的页面作为应用的单独入口点。这将减少每个路由的 bundles 大小,并加快开发环境速度,因为进行更改时只会构建应用的一部分。
例如,如果您有一个页面,/accounts
您可以创建一个文件pages/account.js
并添加相应的重写:
module.exports = {
target: "serverless",
async rewrites() {
return [
// Rewrite everything under `/account/ to `pages/account`
{
source: "/account/:any*",
destination: "/account",
},
// Rewrite everything else to `pages/index`
{
source: "/:any*",
destination: "/",
},
];
},
};
```
# How's this different from using Next.js `getStaticProps` with `getStaticPaths`?
Using `getStaticProps` with `getStaticPaths` allows you to do Static Site Generation (SSG). This means that all the pages in your app are generated as individual `.html`-files when you run `npm run build`.
SSG is awesome, but has a single big limitation: **You need to know all the paths your app has ahead of time**. This is not possible with many apps that have tons of user-specific paths like `/my-payments/123121521241`.
With the approach described in this article, you can use a dynamic router like `react-router` with Next.js just like you would with Create React App or any traditional single-page app.
# Additional Resources:
- [This wonderful gist by @tannerlinsley](https://gist.github.com/tannerlinsley/65ac1f0175d79d19762cf06650707830)