Vue + Vite 项目通过 /dist 子路径访问首页空白问题分析与解决方案
问题分析
当 Vue + Vite 项目部署到 /dist 子路径下出现空白页面时,通常由以下几个原因导致:
1. 静态资源路径配置问题
Vite 默认假设应用部署在根路径下,当部署到子路径时,静态资源路径引用会出错。
2. Vue Router 路径配置问题
如果使用 Vue Router,路由的基础路径需要与部署路径匹配。
3. 服务器配置问题
服务器未正确配置重写规则,导致无法正确处理单页应用的路由。
完整解决方案
方案一:Vite 配置解决方案
1. 更新
vite.config.js
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
// 设置项目根目录
base: '/dist/',
build: {
// 输出目录
outDir: 'dist',
// 资源文件输出路径
assetsDir: 'assets',
// 静态资源引用路径处理
rollupOptions: {
output: {
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
}
}
},
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
server: {
// 开发服务器配置
port: 3000
}
})
2. 更新
vue-router 配置
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
// ...其他路由
]
const router = createRouter({
// 使用历史模式,并设置基础路径
history: createWebHistory('/dist/'),
routes
})
export default router
方案二:环境变量配置
1. 创建环境文件
# .env.production
VITE_APP_BASE_URL=/dist/
VITE_APP_API_BASE_URL=/api/
2. 动态配置 Vite
// vite.config.js
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig(({ mode }) => {
// 加载环境变量
const env = loadEnv(mode, process.cwd())
return {
plugins: [vue()],
// 动态设置 base
base: env.VITE_APP_BASE_URL,
build: {
outDir: 'dist',
// 生成 sourcemap 便于调试
sourcemap: mode === 'development',
}
}
})
3. 在代码中使用环境变量
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 获取基础路径
const basePath = import.meta.env.VITE_APP_BASE_URL || '/'
const app = createApp(App)
app.use(router)
app.mount('#app')
// 输出环境信息便于调试
console.log('Base URL:', basePath)
方案三:HTML 模板配置
1. 更新
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="<%= VITE_APP_BASE_URL %>favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue App</title>
<!-- 动态设置 base 标签 -->
<base href="<%= VITE_APP_BASE_URL %>" />
<!-- 解决相对路径问题 -->
<script type="text/javascript">
// 设置全局基础路径
window.BASE_URL = '<%= VITE_APP_BASE_URL %>';
// 修复 history 路由问题
(function() {
const basePath = '<%= VITE_APP_BASE_URL %>';
if (basePath && basePath !== '/') {
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function(state, title, url) {
return originalPushState.call(this, state, title, basePath + url.replace(new RegExp('^' + basePath), ''));
};
history.replaceState = function(state, title, url) {
return originalReplaceState.call(this, state, title, basePath + url.replace(new RegExp('^' + basePath), ''));
};
}
})();
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
方案四:服务器配置方案
1. Nginx 配置
server {
listen 80;
server_name your-domain.com;
# 主应用路径
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
# 子路径配置
location /dist/ {
alias /usr/share/nginx/html/dist/;
index index.html;
# 处理 Vue Router 的 history 模式
try_files $uri $uri/ /dist/index.html;
# 设置正确的 MIME 类型
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# API 代理
location /api/ {
proxy_pass http://api-server:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
2. Apache 配置
<VirtualHost *:80>
ServerName your-domain.com
DocumentRoot /var/www/html
# 子路径配置
Alias /dist /var/www/html/dist
<Directory "/var/www/html/dist">
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
# 重写规则支持 history 模式
RewriteEngine On
RewriteBase /dist/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /dist/index.html [L]
</Directory>
</VirtualHost>
方案五:构建后验证脚本
1. 创建验证脚本
// scripts/verify-build.js
import fs from 'fs'
import path from 'path'
const distPath = path.resolve(process.cwd(), 'dist')
const indexPath = path.join(distPath, 'index.html')
// 检查构建目录
if (!fs.existsSync(distPath)) {
console.error('❌ dist 目录不存在')
process.exit(1)
}
// 检查 index.html
if (!fs.existsSync(indexPath)) {
console.error('❌ index.html 不存在')
process.exit(1)
}
// 读取 index.html 内容
const htmlContent = fs.readFileSync(indexPath, 'utf-8')
// 检查资源路径
const checks = [
{
name: 'JavaScript 文件引用',
pattern: /src="\/(?!\/)/,
shouldExist: false,
message: '❌ 发现绝对路径引用,应该使用相对路径或基础路径'
},
{
name: '基础路径设置',
pattern: /<base href="\/dist\/"/,
shouldExist: true,
message: '✅ 基础路径已正确设置'
},
{
name: 'Vite 构建标记',
pattern: /data-vite-project-id/,
shouldExist: true,
message: '✅ Vite 构建标记存在'
}
]
console.log('🔍 开始验证构建结果...\n')
checks.forEach(check => {
const hasMatch = check.pattern.test(htmlContent)
const isCorrect = hasMatch === check.shouldExist
if (isCorrect) {
console.log(check.message)
} else {
console.log(`❌ ${check.name} 检查失败`)
}
})
// 检查静态资源
const assetsDir = path.join(distPath, 'assets')
if (fs.existsSync(assetsDir)) {
const assets = fs.readdirSync(assetsDir)
console.log(`\n📁 发现 ${assets.length} 个资源文件`)
}
console.log('\n✅ 验证完成!')
2. 在 package.json 中添加脚本
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"verify": "node scripts/verify-build.js",
"build:verify": "vite build && npm run verify"
}
}
方案六:调试工具与技巧
1. 添加调试中间件
// vite.config.js - 开发服务器配置
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true
}
},
// 添加自定义中间件调试请求
configureServer: (server) => {
server.middlewares.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`)
next()
})
}
}
2. 浏览器控制台调试
// 在应用启动时添加调试信息
console.group('应用环境信息')
console.log('Base URL:', import.meta.env.VITE_APP_BASE_URL)
console.log('Mode:', import.meta.env.MODE)
console.log('Public Path:', __webpack_public_path__ || '未设置')
console.groupEnd()
// 监控路由变化
router.afterEach((to, from) => {
console.log(`路由变化: ${from.path} -> ${to.path}`)
})
最佳实践总结
始终使用环境变量配置基础路径
在开发和构建时使用相同的路径策略
确保服务器正确配置重写规则
使用相对路径或基于 base 的路径引用资源
构建后进行验证和测试
常见问题排查清单
- [ ] 检查
vite.config.js 中的 base 配置
- [ ] 验证
vue-router 的 history 模式基础路径
- [ ] 检查服务器配置中的重写规则
- [ ] 查看浏览器控制台是否有 404 错误
- [ ] 确认静态资源文件是否被正确加载
- [ ] 检查网络请求中的路径是否正确
- [ ] 验证环境变量是否被正确注入
- [ ] 确保所有路由都有相应的处理
通过以上方案,您可以系统地解决 Vue + Vite 项目在子路径部署时的空白页面问题。建议从方案一开始尝试,逐步排查和解决问题。