资讯动态

Vite打包Vue项目部署到Nginx:从配置到避坑全指南

发布时间:2026/9/9 19:25:21 来源:尧图企业网站定制
项目开发完只是第一步让它跑在公网上被别人正常访问才算真正交付。我见过不少前端同学本地npm run dev熟练得很一到打包部署就发怵甚至有人直接把 dist 文件夹拖到服务器上就完事结果各种 404、白屏、接口跨域问题接踵而至。Vite 作为 Vue 项目的新一代构建工具打包速度和生产构建体验确实比 Webpack 时代舒服太多但如果你没搞清楚 Vite 的产物结构和 Nginx 的配置逻辑部署这关照样会卡得你怀疑人生。这篇文章我把 Vite 打包 Vue 项目到 Nginx 这件事拆开揉碎从方案选型到配置细节从打包优化到问题排查一条龙讲透适合刚接触部署、或者部署过但老出问题的朋友照着操作。1. 部署方案选型为什么是 Vite Vue Nginx 这套组合1.1 三件套各自解决什么问题先理清楚这三个角色各管哪一段。Vue 是你用的前端框架负责页面的组件化开发Vite 是构建工具负责把.vue单文件组件、ES Module、TypeScript、SCSS 这些东西编译成浏览器能直接识别的静态文件Nginx 是高性能 Web 服务器负责把编译后的静态文件通过 HTTP 协议吐给用户浏览器同时还能做反向代理、负载均衡、Gzip 压缩、缓存控制这些脏活累活。这套组合之所以主流是因为每一个环节都踩在了点上。Vite 开发服务器基于原生 ESM冷启动速度比 Webpack 快一个量级热更新也是毫秒级响应开发体验非常舒服生产构建底层用的是 Rollup产物干净、Tree Shaking 彻底打包出来的 JS 文件体积在同配置下通常比 Webpack 小。而 Nginx 作为静态文件服务器性能极其强劲单机处理几万并发连接毫无压力配置文件可读性也高不像 Apache 那一堆复杂的指令让人头晕。1.2 部署方案对比为什么不直接用 Node 托管或纯静态服务器你可能会问我能不能不装 Nginx直接在服务器上跑个node server.js或者用 Python 的http.server来托管 dist 文件夹能但都有明显短板。用 Node 原生写静态服务器你需要自己处理 MIME 类型、Gzip、缓存头、history 路由回退这些 Nginx 一个配置块就搞定的事用 Node 手写既重复又容易漏。用python3 -m http.server这类玩具级方案就只能做最基本的文件传输生产环境完全不够看尤其不支持配置 history 路由回退Vue Router 一用 history 模式刷新就 404。相比之下 Nginx 的优势非常明确轻量高效资源占用极低一个默认配置的 Nginx 进程内存占用不到 10MB静态文件服务、反向代理、负载均衡、SSL 终止一气呵成配置语法简单改完nginx -s reload即可生效不需要重启操作系统另一个常见的替代方案是 Docker 部署把 Nginx 打成镜像跑在容器里适合需要标准化交付、多环境迁移的团队。但 Docker 本质上也还是 Nginx只不过多了一层容器封装。我个人的建议是如果你只是一个人维护一个小项目先把裸机部署跑通理解清楚静态文件、反向代理、缓存这几个核心概念再去碰 Docker、Jenkins 那些自动化东西会顺畅得多。2. 环境准备与 Vite 打包核心配置2.1 服务器安装 Nginx搞懂目录结构部署的前提是服务器上得有 Nginx。以 Linux 服务器为例Ubuntu 和 Debian 系可以直接用sudo apt update sudo apt install nginx -yCentOS、Rocky Linux 这类 RedHat 系用sudo yum install nginx -y安装完成后Nginx 的几个关键目录你一定要心里有数路径作用/etc/nginx/nginx.conf主配置文件全局配置一般不建议直接改这里/etc/nginx/conf.d/自定义站点配置目录每个站点一个.conf文件推荐在这里写/usr/share/nginx/html/默认站点根目录刚装完时里面的 index.html 就是欢迎页/var/log/nginx/access.log访问日志谁请求了什么资源都记录在这里/var/log/nginx/error.log错误日志出问题第一个要看的地方启动和检查状态sudo systemctl start nginx sudo systemctl status nginx改完配置以后一定要用nginx -t检查语法看到syntax is ok再nginx -s reload千万不要改完直接 reload语法错了会直接把服务搞挂。2.2 Vite 打包的三大关键配置Vite 项目打包核心配置都在项目根目录的vite.config.js里。很多新手把npm run build一跑dist 出来就直接丢服务器结果样式找不到、图片裂了、路由刷新白屏问题都出在这几个配置上没搞对。第一个是base。这是 Vite 生成资源路径的基准路径默认是/。如果你的站点部署在域名根路径比如https://example.com/用/没问题。但如果你要部署到子路径比如https://example.com/admin/就必须把base改成/admin/否则打包出来的 HTML 里引用的 JS、CSS 路径全都带/assets/...前缀浏览器去域名根目录找资源自然 404。这个坑我见过太多次。第二个是build配置块。outDir控制产物输出目录默认是dist一般不用改。assetsDir控制静态资源子目录默认assets。chunkSizeWarningLimit是 chunk 大小警告阈值默认 500KB项目引了 Element Plus、ECharts 之类的库很容易触发警告可以调到 1500 消除告警但注意这只是一个提示不影响构建结果。第三个是路由模式引发的连锁反应。Vue Router 有两种模式createWebHistory()的 history 模式和createWebHashHistory()的 hash 模式。hash 模式地址栏会有个#比如example.com/#/about这种模式下 Nginx 不需要额外配置因为#后面的内容不会被发到服务器。history 模式地址美观example.com/about但服务器必须配置try_files $uri $uri/ /index.html;做回退否则用户在某一个子路径刷新页面就 404。这两者的取舍要在项目开发初期就定好部署配置完全不一样。一个比较典型的vite.config.jsimport { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ base: /, plugins: [vue()], build: { outDir: dist, assetsDir: assets, chunkSizeWarningLimit: 1500, rollupOptions: { output: { manualChunks: { vue-vendor: [vue, vue-router, pinia], ui-vendor: [element-plus] } } } }, server: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })注意server.proxy只对开发环境有效它解决的是本地开发时前端调接口的跨域问题。生产环境这套代理根本不会生效接口转发必须靠 Nginx这是很多新人容易误解的地方。3. Nginx 部署配置实战3.1 最基础的静态站点配置假设打包产物在项目根目录的dist文件夹里你把它整个上传到服务器/usr/share/nginx/html/下。最简单的 Nginx 配置长这样server { listen 80; server_name example.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } }这里的root指向站点根目录index是默认首页文件。try_files那一行是 history 路由的核心当一个请求进来Nginx 先去找这个 URI 对应的文件找不到就找同名的目录再找不到就回退到/index.html。这样 Vue Router 才能接管前端路由刷新example.com/about时不会返回 404。上传文件的方式我习惯用scp本地终端直接跑scp -r dist/* user你的服务器IP:/usr/share/nginx/html/如果你用的是宝塔面板之类的图形化工具把 dist 内容上传到对应目录也行本质都是一样的。3.2 history 路由模式下的 try_files 回退这里需要展开一下try_files的执行逻辑它是 Nginx 里比较容易让人犯晕的指令。try_files $uri $uri/ /index.html;由三个参数组成Nginx 会按顺序检查$uri当前的请求 URI比如/aboutNginx 会在 root 目录里找有没有about这个文件$uri/如果$uri不是文件再看是不是目录是就返回目录索引/index.html前两个都找不到就把请求重写到/index.html重新走一遍静态文件查找这个机制对前端单页应用来说就是保命条款无论浏览器请求什么路径最终都会落到index.html然后 Vue 的 JS 读地址栏、渲染对应路由组件。如果没有这行配置用户在/about刷新Nginx 去找/about文件找不到就直接 404 了。有几种情况需要调整。如果你部署在子路径比如base: /admin/那么root应该指向 dist 内容的上一级目录而不是直接指向 dist同时try_files的路径也要做适配location /admin/ { alias /usr/share/nginx/html/; try_files $uri $uri/ /admin/index.html; }这个场景相对少见但一旦碰到对路径的理解要求会高不少。建议刚开始部署的朋友都用根路径部署把这个基础流程跑通以后再去折腾子路径。3.3 API 反向代理配置前端接口跨域的关键Vue 项目通常需要请求后端接口比如/api/user/list后端服务跑在http://localhost:8080。如果前端直接发请求给 Nginx 域名的/api/...Nginx 默认只会去静态目录里找同名文件找不到就 404。要解决这个问题必须配置反向代理server { listen 80; server_name example.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }这一段配置的关键点是proxy_pass的写法。当proxy_pass后面不带路径时请求会原样转发浏览器请求/api/user/listNginx 就转发给后端的/api/user/list。如果后端接口本来就带/api前缀这样写最简单直接。但有时候后端接口不带前缀比如后端路由是/user/list希望前端请求/api/user/list时剥掉/api再转发。这时候proxy_pass后面就要加一个斜杠location /api/ { proxy_pass http://localhost:8080/; }带不带结尾斜杠转发结果完全不同proxy_pass 写法前端请求后端实际收到http://localhost:8080/api/user/list/api/user/listhttp://localhost:8080//api/user/list/user/list我每次写这段都下意识确认一下这个坑太经典了。另外proxy_set_header X-Forwarded-For这些头信息是用来传递客户端真实 IP 的后端如果做了日志分析、风控或者限流这些头必须有否则后端看到的所有请求都来自 Nginx 的本机 IP直接炸掉。3.4 Gzip 压缩与静态资源缓存策略前面的配置已经把站点跑起来了接下来是性能优化。Vite 构建出来的 JS、CSS 文件都比较大如果不做压缩用户首次访问要下载几百 KB 甚至几 MB 的资源在弱网环境下体验非常差。Nginx 开启 Gzip 压缩很方便gzip on; gzip_min_length 1k; gzip_comp_level 5; gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svgxml; gzip_vary on;gzip_min_length的意思是小于 1KB 的文件不压缩因为压缩这类小文件反而有额外开销。gzip_comp_level 5是个平衡点压缩级别从 1 到 99 压得最小但耗 CPU实际生产用 5 左右就够了。缓存策略同样重要。Vite 打包后的文件名自带 hash比如assets/index-7f9c4e2b.js这个 hash 是内容级别的代码一改 hash 就变。对这类带 hash 的静态资源可以设置长期缓存浏览器下次访问直接走本地缓存不打服务器location /assets/ { expires 365d; add_header Cache-Control public, immutable; }但是index.html绝对不能这样设置。它是页面入口每一次发布都要保证用户能拿到最新的。给index.html配置 no-cachelocation /index.html { add_header Cache-Control no-cache, no-store, must-revalidate; }这一对配置组合起来即资源强缓存、入口不缓存就能兼顾部署更新和访问速度。如果不设置浏览器可能把旧的index.html缓存住你更新了代码用户却还在跑旧页面排查起来非常费劲。4. 部署过程中的常见问题排查实录4.1 页面刷新 404 或白屏部署完以后访问首页正常但路由跳转某个子页面后一刷新就 404或者直接白屏这是 history 路由模式最典型的问题。原因非常简单刷新时浏览器向服务器发请求请求路径是/aboutNginx 在静态目录里找不到about文件就直接返回 404 了。只要没有配置try_files $uri $uri/ /index.html;这个 404 就会存在。解决方法就是前面说的回退配置。另外一个容易忽略的点是如果你用 hash 模式这个问题天然不存在。但 hash 模式的 URL 不好看我建议正经项目直接用 history 模式然后配好 try_files。白屏还有一种情况刷新后页面渲染出来了但 JS 报错比如Uncaught SyntaxError: Unexpected token 。这个通常是服务器对 JS 请求返回了 HTML 导致的常见于try_files配错JS 文件被错误回退到了 index.html。检查一下location /assets/有没有单独处理以及在浏览器 Network 面板看 JS 的响应体。我实测下来的排查顺序是先看 Network 面板确认静态资源是否 404再看 Console 报错最后看 Nginx 的error.log。多数前端部署问题三步以内就能定位。4.2 静态资源 404base 路径的坑如果页面能打开但 CSS、JS 全部加载失败控制台一堆 404先去看 HTML 里的资源引用路径。打开浏览器按 F12在 Elements 面板看script标签的src是什么。正常的应该是/assets/index-xxx.js。如果看到/assets/index-xxx.js带上了一层奇怪的子路径或者请求打了别的站点那多半是vite.config.js里的base配置跟你实际部署的路径不一致。具体来说项目部署在https://example.com/base设置成/正确项目部署在https://example.com/admin/base设置成/admin/正确项目部署在https://example.com/admin/base还是默认的/错误资源全部从根目录找404项目部署在https://example.com/base设置成./大部分场景能用但要注意带嵌套路由时可能出问题base改成相对路径./是不少人用来绕坑的方法因为这样打包出来的资源路径是相对 HTML 文件的。但对于深层路由页面刷新这种情况相对路径可能会指错位置所以我一般不推荐还是老老实实配绝对路径的base最稳。4.3 接口跨域与代理失效前端页面正常出来了但所有请求都报跨域错误Access-Control-Allow-Origin或者请求直接 404。前者通常是没有配置proxy_pass或者后端服务没有开 CORS后者多半是proxy_pass路径拼接不对走了/api前缀后端实际不认这个路由。如果后端还没加 CORS优先用 Nginx 代理方案解决也就是我前面写的配置。后端服务不需要感知前端的域名所有请求都走同源从根源上避免跨域问题。修改了 Nginx 配置后记得先nginx -t再 reload我用过一次nginx -s reload之前忘了检查语法结果配置文件里少了一个分号整套服务直接崩了。4.4 代码更新了但用户访问还是旧版这个问题大概率是缓存惹的祸。用户浏览器缓存了旧的index.html而新的静态资源文件名虽然变了但 HTML 不会主动去拉取新版本。所以在生产环境index.html必须配置禁用缓存或强校验缓存带 hash 的资源则放心地设置长期缓存。如果已经改了配置但用户还是看到旧版可以让用户强制刷新试试CtrlShiftR或者清除浏览器缓存。更彻底的做法是在 Nginx 配置里对index.html设置Cache-Control: no-cache这样浏览器每次都会去服务器验证一下文件有没有变化有变化就拉新没变化就用缓存兼顾速度和更新。另外还有个隐藏点静态资源长期缓存是建立在文件名带 hash 的前提下的。如果你用的是自定义的rollupOptions输出固定文件名比如output: { entryFileNames: main.js }那每次发布同名文件就会被浏览器强缓存劫持这是个大坑建议避免。4.5 打包内存溢出和 Vite 热更新失效打包过程中偶尔会遇到FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory尤其项目较大、依赖较多时。这是 Node.js 默认堆内存不够用了。解决方案是调整 Node 内存上限export NODE_OPTIONS--max-old-space-size4096 npm run build或者更直接一点把启动脚本改成{ scripts: { build: node --max-old-space-size4096 node_modules/vite/bin/vite.js build } }这里 4096 表示 4GB具体数值根据服务器内存调整别调到比服务器物理内存大否则 OOM Killer 会把进程杀掉构建照样失败。Vite 热更新失效的问题如果你遇到改 vue 文件后浏览器不刷新大概率是缓存坏了。删掉项目根目录下的node_modules/.vite再重启开发服务器90% 的情况都能恢复。如果还不行检查一下你的文件系统是否被某些同步工具干扰以及系统文件监听上限macOS 和 Linux 上文件监听数超过限制也会导致热更新失效。4.6 常见问题速查表问题现象最可能原因解决方向刷新子路径 404缺少 try_files 回退配置location / { try_files $uri $uri/ /index.html; }页面打开但没有样式base 路径不对导致 CSS 404检查并修正 Vite base 配置重新构建接口跨域没有配置反向代理在 Nginx 中配置proxy_pass接口代理 404proxy_pass 结尾斜杠处理不当根据后端实际路由调整 proxy_pass 是否带/用户看到旧版本index.html 被缓存对 index.html 设置 no-cache构建内存溢出Node 堆内存不足配置--max-old-space-size或 NODE_OPTIONS热更新失效Vite 缓存损坏删除node_modules/.vite重启开发服务Nginx 配置语法错误配置文件有错误nginx -t检查修正后 reload5. 部署上线后的几点个人经验部署这套东西一次成功的背后必须有系统性思维。我的经验是先本地验证再上服务器。打包完成后先用npx serve dist在本地起一个静态服务看一眼确认构建产物没问题了再往服务器传。很多人在服务器上抓瞎半天最后才发现是本地打包就有问题白白浪费通信时间。正式上线前一定要检查 Nginx 的 error log。配置没问题不代表运行没问题权限、磁盘、端口占用这些系统层面的问题才是真杀手。比如我遇到过403 Forbidden排查到头是 Nginx 的user没有读取站点目录的权限改一下目录权限就解决。还有一个小技巧如果你在 Nginx 的location块里写了代理但又不确定代理是否生效可以直接在服务器上用curl http://localhost:8080/api/test测试后端接口是否通再curl -H Host: example.com http://localhost/api/test测试 Nginx 转发是否通两步就能把问题圈定在具体环节。关于自动化部署很多团队后面会引入 Jenkins、GitHub Actions 之类的 CI/CD但不管工具多花哨核心链路都是一样的拉代码 → 安装依赖 → 构建 → 上传产物 → reload Nginx。把裸机部署流程吃透自动化只是把这几步串起来的体力活。刚接触的朋友不用急着上自动化先手动部署成功两次理解每一行的意义比盲目抄一个流水线配置有价值得多。最后再分享一个实际操作中的体会Nginx 配置文件和代码一样也要做版本管理。把conf.d下面的配置文件存进 Git每次修改都留痕出问题能回滚。虽然听起来有点小题大做但当你被一个隐藏的配置问题折磨到半夜的时候就知道版本化配置有多香了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价