欢迎光临本地信息咨询网
详情描述
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-routerhistory 模式基础路径
  • [ ] 检查服务器配置中的重写规则
  • [ ] 查看浏览器控制台是否有 404 错误
  • [ ] 确认静态资源文件是否被正确加载
  • [ ] 检查网络请求中的路径是否正确
  • [ ] 验证环境变量是否被正确注入
  • [ ] 确保所有路由都有相应的处理

通过以上方案,您可以系统地解决 Vue + Vite 项目在子路径部署时的空白页面问题。建议从方案一开始尝试,逐步排查和解决问题。

相关帖子
2026年生育津贴发放标准中,对于生育多胞胎的情况是否有额外补贴?
2026年生育津贴发放标准中,对于生育多胞胎的情况是否有额外补贴?
易拉罐拉环的小孔设计背后,隐藏着哪些不为人知的工程学原理?
易拉罐拉环的小孔设计背后,隐藏着哪些不为人知的工程学原理?
2026年高考报名在即,高中学籍状态如何确认以确保资格无误?
2026年高考报名在即,高中学籍状态如何确认以确保资格无误?
2026年多人出行的家庭与团队游客,对农房民宿有哪些新需求?
2026年多人出行的家庭与团队游客,对农房民宿有哪些新需求?
发现社保卡损坏无法使用,正确的处理步骤和更换流程具体是怎样的?
发现社保卡损坏无法使用,正确的处理步骤和更换流程具体是怎样的?
除了拨打110报警,租客还能通过哪些法律渠道尝试追回损失?
除了拨打110报警,租客还能通过哪些法律渠道尝试追回损失?
自由职业者或灵活就业人员,是否可以为自己设立交通通讯补贴以降低税负?
自由职业者或灵活就业人员,是否可以为自己设立交通通讯补贴以降低税负?
2026年,社会舆论和年轻人对彩礼“限高”政策的普遍看法是什么?
2026年,社会舆论和年轻人对彩礼“限高”政策的普遍看法是什么?
计算经济补偿金时,工资基数是指应发工资还是实发工资?
计算经济补偿金时,工资基数是指应发工资还是实发工资?
到2026年,社保“第六险”的评估标准是否更加统一和规范了?
到2026年,社保“第六险”的评估标准是否更加统一和规范了?
贷款期间房产信息变更,讲解信息修改及二次抵押备案调整流程
贷款期间房产信息变更,讲解信息修改及二次抵押备案调整流程
从千兆宽带降级到百兆,运营商要求缴纳高额设备调测费是否合理?
从千兆宽带降级到百兆,运营商要求缴纳高额设备调测费是否合理?
2026年新的食品标识规定,是否让预制菜配料表更透明易懂?
2026年新的食品标识规定,是否让预制菜配料表更透明易懂?
智能汽车记录的行车轨迹信息是否属于个人隐私信息范畴
智能汽车记录的行车轨迹信息是否属于个人隐私信息范畴
如何区分是为了实际需求购物,还是为了短暂的情绪抚慰而进行消费?
如何区分是为了实际需求购物,还是为了短暂的情绪抚慰而进行消费?
2026年,企业的加班审批制度如果过于严格,是否会催生更多隐形加班?
2026年,企业的加班审批制度如果过于严格,是否会催生更多隐形加班?
企业内部非正式的社交活动,如何可能加深某些群体的“隐性”排斥感?
企业内部非正式的社交活动,如何可能加深某些群体的“隐性”排斥感?
对于有过敏性鼻炎的人,在特定季节应如何护理以减少鼻子出血风险?
对于有过敏性鼻炎的人,在特定季节应如何护理以减少鼻子出血风险?
小满节气“苦菜秀”的说法从何而来,这种植物背后有哪些有趣的故事与文化?
小满节气“苦菜秀”的说法从何而来,这种植物背后有哪些有趣的故事与文化?
对维修基金的使用情况有疑问,业主可以通过哪些合法途径进行监督?
对维修基金的使用情况有疑问,业主可以通过哪些合法途径进行监督?
2026年国际原油市场价格波动,主要受全球哪些非政治性供需因素影响?
2026年国际原油市场价格波动,主要受全球哪些非政治性供需因素影响?