问题
在特定页面上出现了JavaScript无法执行且屏幕显示异常的现象。有趣的是,这个问题只在middleware matcher定义的多个路径中的/signup
页面上出现。
typescript
export const config = {
matcher: [
'/',
'/account',
Path.ITEMS, // 这里使用了变量!
'/blog/:path*',
'/user/:path*',
'/signin',
'/signup',
// ...
]
};
原因
Next.js官方文档的解释
根据Next.js官方文档,middleware matcher的值必须在构建时可以静态分析,而变量等动态值会被忽略。
最初,我认为因为Path.ITEMS
是在config正上方用const
声明的,所以不会有问题。但实际上问题依然存在,为了找出原因,我分析了Next.js的源代码。
Next.js的Config分析过程
以下是Next.js实际代码的一部分,展示了分析SSG或middleware等配置时的过程:
typescript
export async function getPageStaticInfo(params: {
nextConfig: Partial<NextConfig>
}): Promise<PageStaticInfo> {
const fileContent = (await tryToReadFile(pageFilePath, !isDev)) || ''
if (/runtime|getStaticProps|getServerSideProps|matcher/.test(fileContent)) {
const swcAST = await parseModule(pageFilePath, fileContent)
const config = tryToExtractExportedConstValue(swcAST, 'config') || {}
// ...
}
}
这段代码中的重要点如下:
- 首先读取配置文件
- 寻找名为
config
的变量 - 生成并解析AST(抽象语法树)
更详细地看,Next.js只允许const
声明:
typescript
export function extractExportedConstValue(
module: Module,
exportedName: string
): any {
for (const moduleItem of module.body) {
if (!isExportDeclaration(moduleItem)) continue;
const declaration = moduleItem.declaration
if (!isVariableDeclaration(declaration)) continue;
if (declaration.kind !== 'const') continue;
// ...
}
}
这段代码表明不是用const
声明的值不会被提取。
SWC的AST和静态分析
Next.js使用SWC(Speedy Web Compiler)来解析代码并生成AST。在这个过程中,使用变量的配置难以进行静态分析,因此在构建时难以优化。
可以进行静态分析的正确配置示例:
typescript
// ✅ 好的示例
export const config = {
matcher: [
'/',
'/account',
'/items', // 直接使用字符串
'/blog/:path*',
]
};
遗留问题
我无法确定为什么其他页面正常工作,而只有/signup
页面出现问题。这可能与Next.js的构建过程或代码分割(code splitting)过程中的特殊性有关。
结论
在使用Next.js middleware matcher时需要注意。为了构建时优化和静态分析,应该使用直接的字符串字面量。