将 Vercel/Nextjs 项目挂载为不同 Vercel/Nextjs 项目的子目录时出现问题



我有一个包含两个项目的单存储库 -webdocs.这些项目中的每一个都是他们自己的Vercel项目,web项目安装在 https://example.com,docs安装在 https://docs.example.com。 所有这些都按预期工作。

我现在想在 https://example.com/docs 上提供docs项目。 在web项目中,我在vercel.json文件中设置了以下重写。

{
"rewrites": [
{
"source": "/docs/:match*",
"destination": "https://docs.example.com/:match*"
},
{ "source": "/(.*)", "destination": "/" }
]
}

这适用于主索引文件,但所有相应的 css 和 js 文件都会导致 404。浏览器正在查找这些文件,https://example.com/_next 这是不正确的,它应该查看 https://docs.example.com/_next。

我该如何完成这项工作?

rewritevs.redirect

让我们首先了解这种差异。

重写仅在服务器上发生。服务器将重写已请求的 URL,并搜索该重写 URL 的内容。客户对此一无所知。例如,如果/a.html 被重写为/b.html,则客户端将在两个 URL 上收到相同的内容。客户端不会知道是否有两次相同的文件,或者一个(或两个)请求是否已重写到其他资源。

另一方面,重定向涉及客户端(即浏览器)。如果要求服务器提供应重定向的 URL,它将使用重写的目标 URL 回复客户端/浏览器。然后,客户端将发送另一个新 URL 请求,并通过更改导航栏中的地址使其对最终用户可见。如果/a.html 重定向到/b.html,客户端在请求 a.html 时不会收到 b.html 的实际内容,但浏览器会将地址更新为 b.html 并发送新的请求。

在您的情况下重写有什么问题?

HTML 包含对使用绝对路径的其他资源的引用,例如:

<script src="/_next/static/..."></script>

如果此文件应作为docs.example.comexample.com/docs提供(例如使用重写),则实际的HTML不会更改。因此,浏览器将分别尝试访问docs.example.com/_next/static/...example.com/_next/static/...。这适用于第一种情况(docs.example.com),但不适用于第二种情况。你已经注意到了。

您可以更改 next 的basePath,例如更改为/docs。然后 HTML 将包含<script src="/docs/_next/...">.这将使浏览器请求分别docs.example.com/docs/_next/...example.com/docs/_next/...。这将适用于第二种情况,但不适用于第一种情况。你可以用更多的重写规则来治愈第一种情况,但我建议一个KISS解决方案。

现在怎么办?

如评论中所述,将完全相同的内容放在两个不同的地址不是好的做法。你可以看到,这也导致了随后的困难。(更不用说搜索引擎对重复内容的惩罚了。

一个好的解决方案是决定存储内容的位置。这应该是docs.example.comexample.com/docs,而不是两者兼而有之。

使用 docs.example.com,将 example.com/docs/转发到 docs.example.com

我建议(并在本节中假设)采取docs.example.com来明确分离关注点。

所以在Vercel中,你将建立两个项目。一个用于您的"主"下一个实例,另一个用于"文档"下一个实例。(两者都可以来自同一个存储库,这无关紧要。

然后,您将域分配给两个项目,例如www.example.com到"主"项目,docs.example.com到"文档"项目。example.com以及docs.example.com现在应该在工作。example.com/docs/应产生 404 错误。

然后,通过添加如下所示的vercel.json,为您的"主"项目添加重定向(而不是重写!):

{
"redirects": [
{ "source": "/docs/:path*", "destination": "https://docs.example.com/:path*" }
]
}

现在,如果您在浏览器中输入example.com/docs/foo,您应该被重定向到docs.example.com/foo并且页面应该正确加载。

仅使用 example.com/docs/

如果您决定仅在example.com/docs/上拥有文档内容,解决方案如下:

  • basePath: '/docs'添加到next.config.js文档下一个项目中。不要向此 vercel 项目添加域。
  • 在"主要"下一个项目中添加一个vercel.json
  • ,如下所示:
{
"rewrites": [
{ "source": "/docs", "destination": "https://$domain-of-docs-project.vercel.app/docs" },
{ "source": "/docs/:path*", "destination": "https://$domain-of-docs-project.vercel.app/docs/:path*" }
]
}

如果您有其他问题或这不能解决问题,请发表评论。

我认为你可以像这样使用vercel.json

{
"rewrites": [
{
"source": "/:path*",
"has": [
{
"type": "host",
"value": "docs.example.com"
}
],
"destination": "/docs/:path*"
}
]
}

Next 为此提供了一个具体的例子。

您的"Web"项目应该处理重写,如下所示。 next.config.js:

const { DOCS_URL } = process.env //this should be the vercel URL NOT docs.example.com
/** @type {import('next').NextConfig} */
module.exports = {
async rewrites() {
return [
{
source: '/:path*',
destination: `/:path*`,
},
{
source: '/docs',
destination: `${DOCS_URL}/docs`,
},
{
source: '/blog/:path*',
destination: `${BLOG_URL}/docs/:path*`,
},
]
},
}

您的"文档"应用需要设置基本路径,如下所示。 next.config.js:

module.exports = {
basePath: '/docs',
}

然后,为了确保 docs.example.com 不再为文档提供服务,我只需删除DNS记录。相反,您只需要一个vercel URL,您将在"web"中的重写中使用。无需从 docs.example.com 指向该服务器。

下一页 多可用区 js 文档

下一个具有多区域的 js 示例

最新更新