vue3app - 完整解决方案与实战教程
在构建 Vue 3 应用(即“vue3app”)的实战中,很多开发者会遇到一个看似简单却极具迷惑性的问题:在开发环境下一切正常,但打包部署后,动态路由刷新页面出现 404,或者使用 history 模式时静态资源路径错乱,导致白屏。这类问题通常出现在将 Vue 3 单页应用部署到 Nginx、Apache 或静态托管服务(如 Vercel、Netlify)时,尤其当项目使用了 Vite 构建、Vue Router 的 history 模式以及动态导入组件时,表现尤为突出。本文将从现象、原因到完整解决方案,帮你彻底排掉这个坑。
问题现象
具体表现为以下几种典型症状:
- 本地
npm run dev时,访问/user/123正常渲染,但执行npm run build后部署到服务器,直接访问该 URL 返回 404。 - 页面刷新后,浏览器控制台报错
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html"。 - 使用
createWebHistory()后,打包产物中的静态资源(如/assets/logo.png)路径变成了绝对路径,但应用部署在子目录(如/my-app/)下,导致图片 404。 - 动态路由
component: () => import('./views/User.vue')在刷新时,网络面板显示请求了错误的 chunk 文件,或者返回了 index.html 内容。
原因分析
这些现象背后其实是三个独立但常被混淆的问题:
- 服务器端路由回退缺失:Vue Router 的 history 模式依赖 HTML5 History API,当用户直接访问
/user/123时,浏览器会向服务器请求该路径。如果服务器没有配置“所有未知路径都返回 index.html”,就会返回 404。这是最核心的原因。 - Vite 的 base 路径配置错误:Vite 默认
base: '/',打包后资源引用为/assets/xxx.js。如果应用部署在非根路径,必须设置base: '/my-app/',否则资源加载失败。 - 动态导入的 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. 排查步骤清单
如果你已经遇到问题,按以下顺序排查:
- 检查
vite.config.ts中的base是否与部署路径一致。 - 检查
createWebHistory()的参数是否使用了import.meta.env.BASE_URL。 - 打开浏览器开发者工具,查看 Network 中失败的请求 URL 是否包含了正确的 base 前缀。
- 检查 Nginx/Apache 是否配置了
try_files或FallbackResource。 - 如果使用 Vercel/Netlify,检查是否添加了
rewrites规则,例如 Vercel 的vercel.json中配置{"rewrites": [{"source": "/(.*)", "destination": "/index.html"}]}。 - 确保
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 在本地模拟生产环境验证一遍。