You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt执行yarn build后.nuxt/dist/client缺失JS资源报404

问题描述
  • 部署场景:使用Apache + PM2部署对接独立域名后端API的Nuxt前端项目,虚拟主机配置完成后可正常访问登录页,但执行yarn build构建后,.nuxt/dist/client目录下预期生成的某一个JS文件始终返回404错误。
  • 特殊现象:将配置中有效的API_URL(原值为https://xxx.xxx/api/v1)替换为无效地址(如https://xxx.xxx/api/1),重新执行yarn build并重启PM2后,404错误消失。
  • 错误截图:
    错误截图
  • 运行环境:
    • 操作系统:Debian 11
    • 服务组件:Apache2 + PM2
现有nuxt.config.js配置
import colors from "vuetify/es5/util/colors";

export default {
  // Global page headers: https://go.nuxtjs.dev/config-head
  head: {
    titleTemplate: "%s - " + process.env.APP_NAME,
    title: process.env.APP_NAME,
    htmlAttrs: {
      lang: "en",
    },
    meta: [
      { charset: "utf-8" },
      { name: "viewport", content: "width=device-width, initial-scale=1" },
      { hid: "description", name: "description", content: "" },
      { name: "format-detection", content: "telephone=no" },
    ],
    link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }],
  },

  watchers: {
    webpack: {
      aggregateTimeout: 300,
      poll: 1000,
    },
  },

  auth: {
    strategies: {
      laravelPassport: {
        provider: "laravel/passport",
        endpoints: {
          userInfo: process.env.API_URL + "/me",
        },
        url: process.env.API_SERVER,
        clientId: process.env.PASSPORT_CLIENT_ID,
        clientSecret: process.env.PASSPORT_CLIENT_SECRET,
      },
      local: {
        endpoints: {
          login: {
            url: "/user/login",
            method: "post",
            propertyName: "data.access_token",
          },
          logout: { url: "/user/logout", method: "post" },
          user: { url: "/me", method: "get", propertyName: "data" },
        },
        token: {
          property: "data.access_token",
        },
        user: {
          property: "data",
        },
        tokenRequired: true,
        tokenType: "Bearer",
        autoFetchUser: true,
      },
      customStrategy: {
        scheme: "~/schemes/customScheme",
        endpoints: {
          login: {
            url: "/user/login",
            method: "post",
            propertyName: "data.access_token",    
          },
          logout: { url: "/user/logout", method: "post" },
          user: { url: "/me", method: "get", propertyName: "data" },
        },
        tokenRequired: true,
        tokenType: "Bearer",
        autoFetchUser: true,
      },
    },
    redirect: {
      login: "/",
      logout: "/",
      home: "/admin",
    },
  },

  router: {
    middleware: ["auth"],
  },

  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [],

  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: ["~plugins/vue-api-query"],

  // Auto import components: https://go.nuxtjs.dev/config-components
  components: true,

  // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
  buildModules: [
    // https://go.nuxtjs.dev/vuetify
    "@nuxtjs/vuetify",
  ],

  // Modules: https://go.nuxtjs.dev/config-modules
  modules: ["@nuxtjs/axios", "@nuxtjs/auth-next"],

  // Vuetify module configuration: https://go.nuxtjs.dev/config-vuetify
  vuetify: {
    customVariables: ["~/assets/variables.scss"],
    theme: {
      dark: false,
      themes: {
        dark: {
          primary: colors.blue.darken2,
          accent: colors.grey.darken3,
          secondary: colors.amber.darken3,
          info: colors.teal.lighten1,
          warning: colors.amber.base,
          error: colors.deepOrange.accent4,
          success: colors.green.accent3,
        },
      },
    },
  },

  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
    maxChunkSize: 300000,
    extend(config, ctx) {
      config.performance.maxAssetSize = 700 * 1024;
    },
  },

  // Axios module configuration: https://go.nuxtjs.dev/config-axios
  axios: {
    baseURL: process.env.API_SERVER || "https://xxx.xxx",
    apiUrl: process.env.API_URL || "https://xxx.xxx/api/v1",
    headers: {
      common: {
        Platform: "web",
      },
    },
  },
};
故障原因
  1. 构建逻辑差异:@nuxtjs/auth-next的Laravel Passport适配逻辑会在构建阶段检测配置的API端点可达性。当API_URL为有效地址时,模块会注入完整的Passport适配代码,触发webpack代码分割生成额外的异步JS chunk;当API_URL为无效地址时,该部分适配代码会被构建工具tree-shaking移除,不会生成对应chunk,自然不会触发404。
  2. Apache配置错误:当前虚拟主机规则没有正确处理Nuxt静态资源请求,存在以下任一问题:/_nuxt/路径的本地目录映射错误,未指向实际的.nuxt/dist/client路径;静态资源请求未配置fallback规则,文件不存在时直接返回404而非转发给PM2托管的Nuxt服务;.nuxt目录权限配置错误,Apache进程无读取权限。
  3. Nuxt配置不规范:使用了已废弃的build.maxChunkSize参数,且在auth配置中硬拼接全量API地址,导致构建阶段环境变量被静态替换,触发非预期的分包行为。
修复方案
  • 修正Apache虚拟主机配置,确保静态资源映射和反向代理规则正确,参考配置如下(将路径、端口替换为实际部署值):
<VirtualHost *:80>
  ServerName 你的前端域名
  DocumentRoot /your/nuxt/project/path/static

  # Nuxt静态资源映射
  Alias /_nuxt/ /your/nuxt/project/path/.nuxt/dist/client/
  <Directory /your/nuxt/project/path/.nuxt/dist/client/>
    Require all granted
    Options FollowSymLinks
    AllowOverride None
  </Directory>

  # 静态资源未命中时回源到Nuxt服务
  <Location /_nuxt/>
    FallbackResource proxy:http://127.0.0.1:3000/_nuxt/
  </Location>

  # 其余请求反向代理到PM2启动的Nuxt服务
  ProxyPass /_nuxt/ !
  ProxyPass / http://127.0.0.1:3000/
  ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>

配置完成后执行systemctl reload apache2重载服务,同时修正目录权限:chown -R www-data:www-data /your/nuxt/project/path/.nuxt。

  • 修正nuxt.config.js不规范配置:

    1. 将auth策略中硬拼接的全量接口地址改为相对路径,统一由axios模块处理baseURL,避免构建阶段静态替换导致的异常分包:把laravelPassport配置下的userInfo: process.env.API_URL + "/me"修改为userInfo: "/me",移除url字段的硬编码全量地址。
    2. 替换废弃的分包参数,将build配置修改为:
    build: {
      extend(config, ctx) {
        config.performance.maxAssetSize = 700 * 1024;
        config.optimization.splitChunks.maxSize = 300000;
      },
    }
    
  • 清理缓存重新构建部署:
    执行以下命令清理旧构建产物和缓存,重新构建后重启服务:

    rm -rf .nuxt node_modules/.cache
    yarn build
    pm2 restart 你的Nuxt服务名称
    

内容的提问来源于stack exchange,提问作者Ian Jay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 07:27:22