vue3app - 完整解决方案与实战教程

在构建 Vue 3 应用(即“vue3app”)的实战中,很多开发者会遇到一个看似简单却极具迷惑性的问题:在开发环境下一切正常,但打包部署后,动态路由刷新页面出现 404,或者使用 history 模式时静态资源路径错乱,导致白屏。这类问题通常出现在将 Vue 3 单页应用部署到 Nginx、Apache 或静态托管服务(如 Vercel、Netlify)时,尤其当项目使用了 Vite 构建、Vue Router 的 history 模式以及动态导入组件时,表现尤为突出。本文将从现象、原因到完整解决方案,帮你彻底排掉这个坑。

问题现象

具体表现为以下几种典型症状:

原因分析

这些现象背后其实是三个独立但常被混淆的问题:

  1. 服务器端路由回退缺失:Vue Router 的 history 模式依赖 HTML5 History API,当用户直接访问 /user/123 时,浏览器会向服务器请求该路径。如果服务器没有配置“所有未知路径都返回 index.html”,就会返回 404。这是最核心的原因。
  2. Vite 的 base 路径配置错误:Vite 默认 base: '/',打包后资源引用为 /assets/xxx.js。如果应用部署在非根路径,必须设置 base: '/my-app/',否则资源加载失败。
  3. 动态导入的 chunk 路径与 publicPath 不匹配:Vue 3 + Vite 使用动态 import 时,chunk 的 URL 由 import.meta.url 或构建时的 base 决定。若 base 配置不当,刷新后请求 chunk 会得到 index.html,从而触发 MIME 类型错误。

解决方案(附完整代码)

下面以最常见的 Nginx 部署 + Vite + Vue Router 4 为例,给出完整配置和代码。

1. 正确配置 Vite 的 base

vite.config.ts 中,根据部署路径设置 base。如果部署在根目录,保持 '/';如果部署在子目录,必须改为子目录路径。

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  // 关键:如果部署在 https://example.com/my-app/,则 base 必须为 '/my-app/'
  // 如果部署在根目录,保持 '/'
  base: process.env.NODE_ENV === 'production' ? '/my-app/' : '/',
  plugins: [vue()],
  build: {
    // 可选:确保 chunk 文件名带 hash,便于缓存
    rollupOptions: {
      output: {
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
      }
    }
  }
})

2. Vue Router 使用 createWebHistory 并传入 base

Vue Router 的 history 模式必须与 Vite 的 base 保持一致,否则路由匹配会出错。

// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue')
  },
  {
    path: '/user/:id',
    name: 'User',
    // 动态导入,刷新时依赖正确的 base
    component: () => import('../views/User.vue')
  }
]

const router = createRouter({
  // 关键:createWebHistory 的参数必须与 vite.config.ts 中的 base 一致
  // 如果 base 是 '/my-app/',这里也要传 '/my-app/'
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router

3. Nginx 配置:所有路径回退到 index.html

这是解决刷新 404 的核心。在 Nginx 的 server 块中添加 try_files

server {
    listen 80;
    server_name example.com;

    # 假设应用部署在 /my-app/ 子目录
    location /my-app/ {
        # 注意:root 或 alias 要指向 index.html 所在目录
        alias /var/www/my-app/;
        index index.html;

        # 关键:先尝试找文件,找不到就回退到 index.html
        # 这样 /my-app/user/123 刷新时不会 404
        try_files $uri $uri/ /my-app/index.html;
    }

    # 如果部署在根目录,则用下面的配置
    # location / {
    #     root /var/www/my-app;
    #     index index.html;
    #     try_files $uri $uri/ /index.html;
    # }
}

4. 排查步骤清单

如果你已经遇到问题,按以下顺序排查:

  1. 检查 vite.config.ts 中的 base 是否与部署路径一致。
  2. 检查 createWebHistory() 的参数是否使用了 import.meta.env.BASE_URL
  3. 打开浏览器开发者工具,查看 Network 中失败的请求 URL 是否包含了正确的 base 前缀。
  4. 检查 Nginx/Apache 是否配置了 try_filesFallbackResource
  5. 如果使用 Vercel/Netlify,检查是否添加了 rewrites 规则,例如 Vercel 的 vercel.json 中配置 {"rewrites": [{"source": "/(.*)", "destination": "/index.html"}]}
  6. 确保 index.html 中的 <script type="module" src="/my-app/assets/index-xxx.js"> 路径正确,这由 Vite 的 base 自动处理。

5. 完整的最小可运行示例(Vue 3 + Vite + Vue Router)

最后给出一个可以直接复用的 main.ts 和路由配置,确保动态导入和 history 模式协同工作。

// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

// 挂载路由
app.use(router)

// 挂载应用
app.mount('#app')
// src/views/User.vue - 动态导入的组件示例
<template>
  <div>
    <h2>User ID: {{ $route.params.id }}</h2>
    <p>刷新此页面不会 404,因为服务器已回退到 index.html</p>
  </div>
</template>

<script setup lang="ts">
// 无需额外逻辑,路由参数通过 $route 获取
</script>

总结:Vue 3 应用部署后的 404 和资源错乱,90% 的情况源于 base 路径不一致服务器缺少 history 回退。只要同时配置好 Vite 的 base、Vue Router 的 createWebHistory 参数以及 Nginx 的 try_files,就能彻底解决。记住:开发环境正常不代表生产环境正常,部署前务必用 npm run build && npx serve dist 在本地模拟生产环境验证一遍。