资讯详情

Vue项目部署Apache实战:解决打包白屏、history路由刷新404

📅 2026/10/16 10:59:34 | 华诺云谱 👁 阅读
Vue项目部署Apache实战:解决打包白屏、history路由刷新404
直接动手写。先说一个很多人都踩过的场景本地npm run dev跑得飞起一打包丢到服务器的apache目录下打开页面白屏、接口404、刷新404甚至css直接加载不出来。其实问题不在你的代码写得怎么样而是“开发环境的思路”没有切到“生产环境的思路”。Vue项目打包部署这件事本质上就三件事把代码构建成浏览器能直接执行的静态文件、把Apache配成能正确托管这些文件的服务、再把路由和接口的“跳转逻辑”跟服务器对齐。这篇文章我按自己的实际部署流程走一遍把你可能遇到的各种坑提前踩平。适合刚接触Vue部署的同学也适合部署过后台管理系统但被history路由折腾过的人。1. 部署前必须理解的打包逻辑1.1 Vite构建到底干了什么Vue项目默认用Vite做构建开发模式是“按需编译热更新”浏览器访问的其实是源码经过实时转换后的内容。但生产环境完全不同你需要执行打包命令Vite会把.vue文件、JavaScript、CSS、图片、字体等资源做压缩、合并、混淆然后输出到一个纯静态目录默认是dist。这个目录里的产物只有一个目的扔到任意一个静态服务器上通过HTTP直接访问。理解这一点很重要——它决定了你很多配置的思维方式。比如你在本地写/api/user这种接口路径开发环境会走Vite的proxy代理转发到后端但打包之后proxy配置就不生效了因为编译产物里没有“Vite服务器”这个概念只有Apache或Nginx这类真实服务器。所以部署前的第一件事就是搞清楚你的“静态资源路径”和“接口请求路径”在服务器环境下应该长什么样。1.2 设定正确的publicPathbase配置Vite打包默认的资源引用路径是/也就是绝对路径。如果你的站点部署在域名根目录比如https://example.com/那没问题。但现实中很多项目是部署在子路径下的比如https://example.com/admin/或者你的Apache虚拟主机配置了类似于Alias /app D:/www/app这样的映射这个时候如果你还是用默认的/作为基准路径所有js和css文件就会去请求https://example.com/assets/xxx.js而服务器上根本不存在这个路径最终结果就是白屏。解决办法是在vite.config.js里设置base// vite.config.js export default defineConfig({ base: ./, // 相对路径适配任意子目录部署 // 或者写具体的子路径 // base: /admin/ })我自己的习惯是如果只有一个独立站点直接用/清晰干净如果是要部署到子目录或者以后可能迁移就用./。相对路径的好处是无论你把它丢到/app还是/test下面资源引用都能自动适配不会出路径问题。但注意如果你用了createWebHistory这种HTML5 History路由模式base配置只影响静态资源的引用不影响路由地址两者要分开处理。1.3 生产环境接口地址单独用环境变量管理打包部署最忌讳的就是把接口地址写死在代码里。尤其是前端项目对接的后端接口开发环境可能是http://localhost:8080测试环境是http://test-api.example.com生产环境又是https://api.example.com。如果你在代码里写死任意一个换环境就要重新改代码重新打包。我习惯在项目根目录新增一个.env.production文件# .env.production VITE_API_BASE_URLhttps://api.example.com然后在代码里统一封装请求基础路径比如用axios时// src/utils/request.js import axios from axios const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 })这样打包时就把VITE_API_BASE_URL替换成对应环境地址。要注意的是import.meta.env暴露给前端代码的变量必须以VITE_开头否则拿不到。如果你发现打包后接口请求还是localhost先看看命名是不是没遵守这个规则。2. Apache服务器核心配置拆解2.1 确认Apache的模块加载情况Apache能托管Vue项目依赖几个关键模块mod_dir目录访问、mod_rewriteURL重写、mod_mime文件类型识别。大部分Apache发行版默认都装了但Windows下的Apache或者某些精简安装包可能没启用或没编译。在httpd.conf里检查这几行是否被注释掉LoadModule rewrite_module modules/mod_rewrite.so LoadModule mime_module modules/mod_mime.so LoadModule dir_module modules/mod_dir.so如果找不到这些行说明你的Apache可能没有编译mod_rewrite需要重装或换发行版。如果你用的是宝塔面板这类集成环境一般可以在“软件管理 → Apache → 模块管理”里看到启用的模块列表。我部署时最喜欢直接写.htaccess而不改主配置因为虚拟主机的配置改动容易影响其他站点而.htaccess只在当前目录生效出了问题只影响当前应用回滚也简单。2.2 httpd.conf vs .htaccess优先级和适用场景Apache的配置规则有个顺序主配置httpd.conf或conf.d下的文件优先.htaccess是“分布式配置文件”在请求进入时逐级读取执行优先级最低但可以覆盖部分主配置。换句话说如果主配置里已经设置了AllowOverride None那么.htaccess文件会被完全忽略。所以部署Vue项目时第一件事就是确保项目所在目录开启了AllowOverride AllDirectory D:/www/app Options Indexes FollowSymLinks AllowOverride All Require all granted /Directory如果你有权限管理服务器主配置我建议用虚拟主机的方式部署清晰又独立如果你用的是虚拟主机或托管环境没法改主配置那.htaccess就是几乎唯一的选择。2.3 配置虚拟主机托管Vue应用虚拟主机配置有两种常见写法基于域名和基于端口。基于域名的适合真实上线本地调试或内网测试则常用端口区分。一个比较标准的Vue项目虚拟主机配置大概是这样的VirtualHost *:80 ServerName myapp.example.com DocumentRoot D:/www/app/dist Directory D:/www/app/dist Options Indexes FollowSymLinks AllowOverride All Require all granted /Directory ErrorLog logs/myapp-error.log CustomLog logs/myapp-access.log common /VirtualHost这里的关键点有两个DocumentRoot必须指向打包产物目录也就是dist而不是源码目录Directory权限必须设置正确否则会报403 Forbidden。注意403错误里最容易被忽略的是Require all granted。Apache 2.4开始默认的授权逻辑和2.2完全不同老教程里写Order allow,deny的配置在新版本里已经被标记为过时强烈建议跟着2.4的语法走。3. 打包构建与路由器模式选择3.1 执行构建与产物检查在你的Vue项目目录下执行npm run build构建完成后dist目录下会生成index.html、assets文件夹等。建议在部署前先看一眼index.html里的资源引用路径script typemodule crossorigin src/assets/index-xxxx.js/script如果src是以/开头的说明base是绝对路径根目录如果是./开头就是相对路径。这一步能帮你提前判断部署之后静态资源到底会从哪里加载。还有一个经常被忽略的东西public目录里的文件。Vite会把public目录下的文件原封不动地复制到dist根目录比如favicon.ico、robots.txt、CNAME等。如果你发现某张图片或某个静态文件打包后找不到了先看它是不是放在src/assets里被构建处理了而不是放在public里。3.2 createWebHistory和createWebHashHistory怎么选Vue Router提供两种路由模式createWebHistoryHistory模式和createWebHashHistoryHash模式。它们的区别在于URL表现和服务器要求。Hash模式URL形如/index.html#/user/list#后面的内容不会发送到服务器所以服务器不需要做任何额外配置就能支持刷新。但缺点是不美观且部分场景如微信支付回调对URL有严格校验可能出问题。History模式URL形如/user/list是标准路径。看起来最正常但服务器必须把所有前端路由都重写到index.html否则刷新或直接访问深层路径会404。我个人的习惯对外面向用户的系统只要服务器配置能跟上一律用History模式。仅内部工具或没法配置服务器的静态托管场景才考虑Hash模式。但这里没有对错只有适合不适合。3.3 为什么刷新会404深入理解服务器回退逻辑假设你部署了History模式的路由用户访问https://example.com/user/1服务器收到请求后会去DocumentRoot下找user/1这个路径——当然找不到因为它不在磁盘上是前端路由管理的内容。Apache的mod_rewrite能做一件事当请求的路径在文件系统中不存在时把请求内部重写到index.html。这样前端路由接管之后由Vue Router根据URL渲染对应页面。规则如下IfModule mod_rewrite.c RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] /IfModule我来解释一下这三行的意思RewriteRule ^index\.html$ - [L]直接访问index.html时不再重写避免循环。RewriteCond %{REQUEST_FILENAME} !-f如果请求的是真实存在的文件如JS、CSS、图片直接放行。RewriteCond %{REQUEST_FILENAME} !-d如果请求的是真实存在的目录直接放行。RewriteRule . /index.html [L]其余所有情况一律重写到index.html。这个规则的核心思想就是先放走所有真实静态资源剩下的全部交给前端路由。4. 实操部署全流程4.1 完整配置文件示例以一台Linux服务器为例我把整个部署过程中涉及的配置整理成一个可直接套用的模板。Apache站点配置文件一般放在/etc/httpd/conf.d/或/etc/apache2/sites-available/下按你的发行版选择。VirtualHost *:80 ServerName vue-deploy-demo.example.com DocumentRoot /var/www/vue-app/dist Directory /var/www/vue-app/dist Options -Indexes FollowSymLinks AllowOverride All Require all granted /Directory # 禁止访问隐藏文件 FilesMatch ^\. Require all denied /FilesMatch ErrorLog ${APACHE_LOG_DIR}/vue-app-error.log CustomLog ${APACHE_LOG_DIR}/vue-app-access.log combined /VirtualHost对应这份Vue项目根目录下新建的.htaccessIfModule mod_rewrite.c RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] /IfModule # 静态资源缓存策略可选 IfModule mod_expires.c ExpiresActive On ExpiresByType text/css access plus 7 days ExpiresByType application/javascript access plus 7 days ExpiresByType image/png access plus 30 days /IfModule注意index.html的缓存单独处理index.html不要设长缓存否则你发新版后用户始终拿到旧壳。一般建议index.html设置为no-cache。4.2 上传构建产物的几种方式构建产物上传服务器核心是把dist目录完整同步到DocumentRoot指向的位置。结合我自己的实操经验推荐以下方式FTP/SFTP最直观适合小项目。用FileZilla这类工具把dist目录拖上去就行。但要注意上传前最好清空目标目录里的旧文件避免历史垃圾文件干扰尤其当你改过资源名或删除过页面时旧的JS/CSS残留在服务器上会导致浏览器加载到冲突版本。rsyncLinux服务器上最常用的增量同步工具效率高、支持压缩传输而且只传变更文件。rsync -avz --delete dist/ useryour-server:/var/www/vue-app/dist/这里的--delete一定要加它表示同步时删除远端多余的文件保证两端目录完全一致。Git钩子/CI/CD如果你的项目已经纳入版本管理可以配置Webhook或GitLab CI/CD在代码合并或打Tag时自动执行npm run build并同步到服务器。这种方法最适合生产环境频繁发版的团队能避免人为操作的遗漏。我的建议是上线前期用SFTP 手动清目录就好因为这时候改动频繁出现问题方便排查真正稳定了再上CI/CD。不要一开始就追求全自动化自动化程度越高排障链路越长。4.3 常见部署场景子目录部署还有一种很常见的场景一台服务器上放了多个Web应用Vue项目只是其中一个子目录比如http://example.com/oa/。这种情况下Vite的base要设为/oa/Apache配置要新增Alias映射。Alias /oa/ /var/www/oa-app/dist/ Directory /var/www/oa-app/dist Options -Indexes FollowSymLinks AllowOverride All Require all granted /Directory同时.htaccess里的RewriteBase也要改成/oa/。如果你忘了改RewriteBase会出现一种很诡异的情况首页能访问点击跳转到其他页面也没问题但一刷新就404。原因就是重写出来的目标路径不对服务器把请求重写到了根目录的index.html而根目录根本没有这个文件。还有一种偷懒的做法是直接把.htaccess里的内容改成RewriteRule . /oa/index.html [L]效果一样只是写死了路径。5. 常见问题与排查方法5.1 白屏JS加载失败还是路由没匹配白屏是Vue部署后最常遇到的问题。打开浏览器开发者工具找到Console面板和Network面板判断属于哪种情况。如果Console报错Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of text/html说明服务器对.js文件的响应不是JavaScript类型而是被重写成了index.html。这通常意味着你的Rewrite规则里的!-f条件不生效或Apache未正确加载mod_mime。检查两点静态文件是否真的存在于目标目录。如果dist里的JS文件名是index-abc123.js但服务器目录下确实没有这个文件那Rewrite规则判断“不是真实文件”然后正常重写到index.html就会出现MIME类型错误。用浏览器直接访问该JS路径看看返回的是文件内容还是HTML页面。如果Console报错是某个JS运行时错误比如Uncaught TypeError: Cannot read properties of null那一般不是部署问题而是代码里某处拿不到DOM节点属于业务代码问题得回开发环境复现。提示白屏的时候不要只盯着屏幕按F12看Network里哪些请求是红色往往一眼就能定位。5.2 history路由刷新404的三种原因刷新404基本可以锁定在Rewrite规则或服务器配置层面常见原因有三个第一.htaccess没有生效。检查AllowOverride是否真的为All。很多教程只给.htaccess内容不提这个前置条件结果大家复制粘贴后死活不生效。第二REWRITE模块没有加载。Windows下的Apache经常出现这种情况。第三RewriteBase写错。尤其是子目录部署时RewriteBase /会导致所有重写路径都指向根目录404自然就来了。排查时可以在.htaccess里放一段测试代码试探RewriteRule ^test$ /test.html [R302,L]然后访问/test如果浏览器跳转到了/test.html说明Rewrite没问题如果还是404说明模块或配置压根没生效。5.3 缓存导致线上永远“还是旧版本”有时候你明明上传了新代码用户或你自己刷新后看到的还是旧版本。这个坑比404还隐性。原因可能有两个一是服务端缓存。Apache的mod_cache模块或反向代理层比如CDN对静态资源做了长缓存导致浏览器拿到的还是旧文件。二是浏览器缓存。尤其index.html被浏览器、服务端都缓存后整个页面入口就是旧壳里面的JS引用也是旧的。解决方案给index.html设置Cache-Control: no-cache让它始终回源验证给带hash的静态资源设置长缓存通常一年都没问题因为文件名变了就是新文件变了就自然会加载新文件。Apache配置可以在.htaccess里加FilesMatch index\.html$ Header set Cache-Control no-cache, no-store, must-revalidate /FilesMatch FilesMatch \.(js|css|png|jpg|woff2)$ Header set Cache-Control public, max-age31536000, immutable /FilesMatch注意Header指令需要mod_headers模块支持。这套方案我在实际项目里用来处理“用户永远看到旧版本”投诉效果很好。5.4 接口404或跨域问题部署后页面能打开但所有接口请求都404或超时这个也很常见。原因一般是后端接口没部署到位或路径不对。后端服务跨域限制前端请求被拦截。你在代码里把VITE_API_BASE_URL配成https://api.example.com但服务器那边没允许你的前端域名跨域访问就会出CORS错误。让后端配合放开跨域// Node.js后端示例 res.setHeader(Access-Control-Allow-Origin, https://your-frontend-domain.com) res.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS) res.setHeader(Access-Control-Allow-Headers, Content-Type, Authorization)如果你的前后端部署在同一台Apache上还可以通过反向代理的方式解决跨域问题。比如前端在/api开头的请求直接代理到后端的127.0.0.1:8080ProxyPass /api/ http://127.0.0.1:8080/ ProxyPassReverse /api/ http://127.0.0.1:8080/这样前端代码里baseURL直接写/api即可浏览器同源策略不会拦截后端也不需要处理CORS。这种方案在生产环境里比“前端直连后端接口”更省心还能顺便隐藏后端服务器地址。5.5 Windows环境部署的专属坑如果你是在Windows上用Apache部署有几个额外问题特别容易踩路径分隔符Apache配置文件里路径建议用正斜杠/或双反斜杠别用单个反斜杠容易歧义。端口占用80端口经常被其他程序占用比如IIS、Docker启动Apache会失败。可以改用8080端口或者把IIS停掉。目录权限Windows下Apache默认以某个系统用户运行偶尔会出现“有权限但页面403”的情况需要检查目录的“安全”选项卡里是否给了Users组读取和运行权限。中文路径千万不要把Vue项目放在带中文或空格的目录里Apache解析路径时偶尔会抽风尤其是旧版本。6. 部署完成后的自检清单经历过几次“改完配置、重启服务、还是不行”折磨之后我整理了一个自检清单每次部署完按顺序过一遍直接访问域名根目录页面首页是否正常渲染。按F12查看Console是否有红色报错。查看Network里首个HTML请求的状态码和资源引用路径。直接访问一个深层路由如/user/1确认刷新不404。检查任意带hash的JS/CSS请求是否返回200并正确加载。登录页面走一遍登录流程确认接口请求地址和响应正常。如果以上都通过这次部署基本就算稳了。如果有一项不过也别慌按上面几个小节的方式逐步排查。7. 从部署延伸出去的几点建议Vue打包部署只是把应用“送上线”的第一步。实际运维中后面还有持续发布、版本回滚、日志排查等一堆事。这里根据自己的经验提几个建议。第一发布记录一定要做。每次部署前记一下版本号、变更内容、时间出问题的时候能快速定位“这次改了什么导致线上挂掉”。第二保留上一次的构建产物别急着删。发布后如果发现线上有严重Bug把上一个版本的dist目录直接恢复回去比修复代码再构建再上传快得多。我的做法是在服务器上保留一个release_20240101这样的备份目录紧急情况一键切回。第三日志是排查问题的最可靠信息源。Apache的错误日志和访问日志里几乎记录了你能遇到的所有异常。部署前确认ErrorLog和CustomLog已经打开路径你知道在哪。很多线上问题看日志比猜快得多。第四有条件的话做一下HTTPS。Apache开启SSL非常简单配置好证书后你的站点就同时支持HTTP和HTTPS访问。如果前后端都在HTTPS下接口跨域也少很多麻烦。部署Vue项目时把80端口的HTTP流量做一个301跳转到443也是一种标准做法VirtualHost *:80 ServerName myapp.example.com Redirect permanent / https://myapp.example.com/ /VirtualHost如果你用的是Lets Encrypt这类免费证书续期脚本写好后基本不用操心稳定得很。再补充一个体验优化的小技巧打包时开启Gzip压缩可以显著减少传输体积。如果你的Apache支持mod_deflate在.htaccess里加几行配置就行。IfModule mod_deflate.c AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json /IfModuleVite打包出来的JS本身就是压缩过的但文本类型的Gzip还能再少一大截。实测大部分Vue项目打包后的gzip体积约为原始体积的25%35%对页面加载速度的提升非常明显。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑