欢迎光临本地信息咨询网
详情描述
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 项目在子路径部署时的空白页面问题。建议从方案一开始尝试,逐步排查和解决问题。

相关帖子
公务员车贷额度有优势?职业身份对放款金额影响
公务员车贷额度有优势?职业身份对放款金额影响
2026年生育津贴发放标准中,对于生育多胞胎的情况是否有额外补贴?
2026年生育津贴发放标准中,对于生育多胞胎的情况是否有额外补贴?
易拉罐拉环的小孔设计背后,隐藏着哪些不为人知的工程学原理?
易拉罐拉环的小孔设计背后,隐藏着哪些不为人知的工程学原理?
2026年高考报名在即,高中学籍状态如何确认以确保资格无误?
2026年高考报名在即,高中学籍状态如何确认以确保资格无误?
2026年多人出行的家庭与团队游客,对农房民宿有哪些新需求?
2026年多人出行的家庭与团队游客,对农房民宿有哪些新需求?
发现社保卡损坏无法使用,正确的处理步骤和更换流程具体是怎样的?
发现社保卡损坏无法使用,正确的处理步骤和更换流程具体是怎样的?
马上消费金融利率,安逸花借款利息计算
马上消费金融利率,安逸花借款利息计算
在涉及遗产继承或抚养权纠纷时,出生医学证明能提供怎样的法律依据?
在涉及遗产继承或抚养权纠纷时,出生医学证明能提供怎样的法律依据?
智能玩具联网功能背后的数据安全与儿童隐私保护,家长应了解哪些知识?
智能玩具联网功能背后的数据安全与儿童隐私保护,家长应了解哪些知识?
个人债务优化重组申请流程有哪些步骤?从申请到落地全解析
个人债务优化重组申请流程有哪些步骤?从申请到落地全解析
更年期后,女性在职业生涯和个人兴趣发展上可能迎来哪些新机遇?
更年期后,女性在职业生涯和个人兴趣发展上可能迎来哪些新机遇?
汽车贷款期限包含宽限期吗?还款日与期限的计算
汽车贷款期限包含宽限期吗?还款日与期限的计算
除了便捷性,预制菜在执行的食品安全标准上与餐厅现做菜有何异同?
除了便捷性,预制菜在执行的食品安全标准上与餐厅现做菜有何异同?
写字楼门店抵押贷款负债率高怎么办?降低负债率提高批贷成功率
写字楼门店抵押贷款负债率高怎么办?降低负债率提高批贷成功率
商铺门面房抵押贷款保险必须买吗?财产保险与抵押贷款保险区别
商铺门面房抵押贷款保险必须买吗?财产保险与抵押贷款保险区别
面对2026年的市场环境,独立开发者应聚焦解决用户的哪些痛点?
面对2026年的市场环境,独立开发者应聚焦解决用户的哪些痛点?
别墅抵押贷款LPR怎么影响利率?LPR调整与月供变化关系解读
别墅抵押贷款LPR怎么影响利率?LPR调整与月供变化关系解读
如何通过角色扮演类玩具,有效培养孩子的同理心与情绪管理能力?
如何通过角色扮演类玩具,有效培养孩子的同理心与情绪管理能力?
过桥垫资让与担保效力?名为买卖实为借贷
过桥垫资让与担保效力?名为买卖实为借贷
别墅抵押贷款可以抵押给典当行吗?典当行别墅抵押贷款利弊
别墅抵押贷款可以抵押给典当行吗?典当行别墅抵押贷款利弊
公司经营贷款集团授信流程,企业集团统一授信
公司经营贷款集团授信流程,企业集团统一授信