调试模式
通过设置环境变量 OPEN_NEXT_DEBUG=true
可以启用 OpenNext 的调试模式。
这将向控制台输出大量额外的日志信息。同时会禁用 esbuild 的代码压缩,并为输出添加 source map。这可能导致生成的代码体积比生产环境构建大 2-3 倍。请勿在生产环境中启用此模式
OPEN_NEXT_DEBUG=true npx open-next@latest build
无法找到 next 模块
你可能会在 CloudWatch 日志中看到这个错误:Cannot find module 'next'
。这通常发生在 monorepo 项目中且存在多个锁文件的情况下。请确保项目根目录下只有一个锁文件。
减小打包体积
Next.js 可能会错误地将某些依赖包含在打包文件中。你可以通过以下 next.config.js
配置来移除它们:
outputFileTracingExcludes: { // 或 Next < 15 版本中使用 experimental.outputFileTracingExcludes
"*": ["node_modules/the-unwanted-package"],
},
另外,除非绝对必要,否则不应将 sharp 添加为依赖项,因为图像优化功能已经自带了特定版本的 sharp。
移除 source map
Source map 可能会显著增加打包体积,从独立输出中排除它们可能很有价值。 例如,Sentry 不会删除服务器端的 source map(即使 "deleteSourcemapsAfterUpload" 设为 true),而只会删除客户端的 source map。查看 Sentry 源代码 (opens in a new tab)。
你可以用类似方式排除 source map:
outputFileTracingExcludes: {
"*": [
'./**/*.js.map',
'./**/*.mjs.map',
'./**/*.cjs.map'
],
},
为 ISR 修补 fetch 行为(仅适用于 next@13.5.1+ 版本)
如果在应用中使用 ISR(增量静态再生)和 fetch,可能会遇到一个导致 revalidate 值不一致的 bug。该问题的表现是:页面会使用所有 fetch 调用中最低的 revalidate 值进行重新验证,而忽略各自的设定值。要修复此问题,需要在根布局组件中按以下代码片段修改 fetch 函数:
export default function RootLayout() {
const asyncStorage = require('next/dist/client/components/static-generation-async-storage.external');
//@ts-ignore
const staticStore =
(fetch as any).__nextGetStaticStore?.() ||
asyncStorage.staticGenerationAsyncStorage;
const store = staticStore.getStore();
store.isOnDemandRevalidate =
store.isOnDemandRevalidate && !(process.env.OPEN_NEXT_ISR === 'true');
return <>...</>;
}
页面刷新和直接访问 URL 时的路由访问拒绝错误
当刷新动态/静态路由或直接从 URL 访问类似 /profile/[userId]/[id]
的路由时,可能会收到如下 XML 格式的 Access Denied
错误:
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>R4E6T9G2Q1S0Z5X8</RequestId>
<HostId>S7h9F3g2T0z5K8d6A2s1W4x3C7v8B9m2L0j3K4i7H8g9F0r3A5q8w9E8r7t6Y5h4U3i2O1p0</HostId>
</Error>
在使用 App Router 时,如果通过 NextJS 的 <Link>
组件进行客户端导航也可能出现此问题。
可能的原因是 public
目录中存在与路由名称冲突的文件夹或文件。此时应重命名这些冲突的文件/文件夹。
无法找到模块 './chunks/xxxx.js'
错误
如果在 instrumentation.ts
中使用动态导入,会在运行时引发此错误。移除动态导入即可解决该问题。
Sentry 服务端配置
Sentry 文档推荐的配置在 instrumentation.ts
中使用动态导入,这会导致上述错误。
以下是可解决该问题的有效 Sentry 配置:
instrumentation.ts
import * as Sentry from "@sentry/nextjs";
import { initSentry } from "../sentry.server.config";
export const onRequestError = Sentry.captureRequestError;
export async function register() {
initSentry(process.env.NEXT_RUNTIME as "nodejs" | "edge");
}
sentry.server.config.ts
import * as Sentry from "@sentry/nextjs";
export const initSentry = (runtime: "nodejs" | "edge") => {
Sentry.init({
dsn: "https://...",
//...其余配置项
});
};
AWS Lambda 流式响应中的空体问题
我们过去曾遇到过在 AWS Lambda 中当响应体为空时流式传输会挂起的问题。
目前 OpenNext 中有一个临时解决方案,通过设置环境变量 OPEN_NEXT_FORCE_NON_EMPTY_RESPONSE
为 true
。
这会在流中写入一些内容来确保它不是空的。
Yarn Plug'n'Play 清单禁止导入 "xxx" 的错误
这个错误通常可以通过删除仓库中所有 yarn 相关文件来解决。你还应该检查 package.json
中是否将 yarn
设置为 packageManager
,删除该配置可以解决问题。
如果你使用 yarn
,这里有一个临时解决方案 (opens in a new tab)。
如果你没有使用 yarn
但看到了与 yarn
相关的错误,可以尝试运行 corepack disable
或将 nvm
更新到 0.40.2
版本。
我的打包文件中缺少某个文件/依赖项
有时您的 server functions 打包文件中可能会缺少某些文件。例如可能是 sentry.server.config.ts
文件。这可以是任何文件或目录,也支持 glob 模式匹配。在 Next.js 中有一个选项可以包含未被自动追踪到的文件,这个选项叫做 outputFileTracingIncludes
。以下是在 next.config.ts
中使用它的示例:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
outputFileTracingIncludes: {
"*": ["sentry.server.config.ts"],
// 也可以使用 glob 模式
"/api/*": ["node_modules/.prisma/client/**/*"],
},
};
export default nextConfig;
这会将文件复制到 .open-next/server-functions/default/sentry.server.config.ts
,或者在函数拆分的情况下复制到每个拆分函数中。要了解更多关于 outputFileTracingIncludes
的信息,可以参考 Next.js 文档 (opens in a new tab)。
该功能在 OpenNext 的函数拆分场景下同样适用。如果您的键对应特定路由(例如 api/test
),则该文件只会被包含在该路由的函数打包中。而使用 *
作为键时,文件会被包含在所有函数打包中。以下是函数拆分的示例:
// open-next.config.ts
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next";
const config = {
default: {},
functions: {
extraFunction: {
patterns: ["api/test"],
// 这是该函数将使用的路由
routes: ["app/api/test/route"],
override: {
wrapper: "aws-lambda-streaming",
},
},
},
} satisfies OpenNextConfig;
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
outputFileTracingIncludes: {
// 这些文件只会被复制到 extraFunction 的打包中
"/api/test": ["sentry.config.ts", "node_modules/.prisma/client/**/*"],
},
};
该功能在 monorepo 中同样有效。假设您的 Next 应用位于 packages/web
目录下,文件将被写入到:packages/web/.open-next/server-functions/default/packages/web/*