为什么不能在中间件的匹配器中使用变量?

🌐

本帖由 DeepL 翻译。如有任何翻译错误,请告知我们!

问题

在某些页面上,JavaScript 无法执行,屏幕会崩溃。有趣的是,在中间件匹配器定义的不同路径中,只有/signup页面出现了这个问题。

Image.png

export const config = {
  matcher: [
    '/',
    /account'、
    Path.ITEMS, // 在此使用变量!
    '/blog/:path*'、
    '/user/:path*'、
    /signin'、
    /signup'、
    // ...
  ]
};

原因

来自 Next.js 官方文档的解释

根据 Next.js 官方文档,中间件匹配器中的值必须在构建时可静态解析,变量等动态值将被忽略。

起初,我以为既然我在 config 上声明了Path.ITEMS 为 const,就不会有问题了。但实际上,我遇到了问题,于是我分析了 Next.js 的源代码,以找出原因。

分析 Next.js 中的 config

以下是 Next.js 的部分实际代码,分析 SSG 或中间件配置的过程如下

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') || {}
    // ...
  }
}

这段代码的要点是

  1. 首先读取配置文件
  2. 查找名为config的变量
  3. 创建并解析抽象语法树 (AST)

仔细观察,Next.js 只允许常量声明

导出函数 extractExportedConstValue(
  module: 模块
  exportedName: string
): any {
  for (const moduleItem of module.body) {
    if (!isExportDeclaration(moduleItem)) continueconst declaration = moduleItem.declaration
    if (!isVariableDeclaration(declaration)) continueif (declaration.kind !== 'const') continue// ...
  }
}

这段代码表明,未声明为 const 的值不会被提取出来。

SWC 的 AST 和静态分析

Next.js 使用 Speedy Web Compiler (SWC) 解析代码并生成 AST。在此过程中,很难对使用变量的设置进行静态分析,因此很难在构建时对其进行优化。

可静态分析的正确配置示例:

// ✅ 示例
export const config = {
  matcher: [
    '/',
    '/account''/items', // 使用直接字符串
    '/blog/:path*'、
  ]
};

剩余问题

我们还没弄明白为什么/signup页面会出现问题,而其他页面却运行正常。这可能与 Next.js 的构建过程或代码拆分有关系。

结论

在 Next.js 中间件匹配器中使用变量时需要小心。你应该使用直接字符串来进行构建时间优化和静态分析。