ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Windows下用Nginx部署Vue项目:动静分离配置与避坑指南

Windows下用Nginx部署Vue项目:动静分离配置与避坑指南 第一次在Windows机器上部署Vue项目我干过一件蠢事把build出来的dist文件夹整个拖到服务器双击index.html然后对着白屏愣了半天。后来才明白生产环境的玩法完全不是这样——你要用Nginx把Vue打包出来的静态文件托管起来再把API请求反向代理给后端服务这个过程就是大家常说的动静分离。这篇文章我会把Windows下用Nginx部署Vue的完整流程、动静分离配置的每个细节、以及我在实际部署中踩过的坑一次讲透保证你按着做能从dist文件夹一路跑到线上可访问。1. 为什么生产环境要多一个Nginx动静分离到底在解决什么问题1.1 开发环境的假象与生产环境的现实先想一个问题为什么开发的时候一切正常部署到服务器就各种白屏因为开发时跑的是npm run dev底层是个开发服务器webpack-dev-server / vite dev server它负责把代码实时编译成浏览器认识的样子还带热更新。这个过程很重、很慢只适合开发时一个人用扛不住真实用户流量。生产环境需要的是把构建出来的静态文件HTML、JS、CSS、图片用尽可能快的方式吐给用户同时把登录、查询、提交这类动态请求安全地转发给后端程序。这两件事恰好都是Nginx的强项。1.2 动静分离到底分的是什么用大白话拆开静图片、JS、CSS、字体、页面本身这些是构建后不会变的文件谁访问都是一样的内容。动/api/login、/api/user/list这类接口请求背后有真实业务逻辑要读数据库、做计算。动静分离就是在Nginx里把这两类请求分开处理静态内容由Nginx直接读取磁盘返回效率极高动态请求通过proxy_pass转发给后端服务Spring Boot、Node、Python等后端只处理自己该处理的业务。这么做的收益也很直接后端服务不再需要处理静态文件的读写压力小一个数量级。前端资源有独立发布通道改页面不用重启后端。Nginx对静态文件的高并发处理能力远强于一般应用服务器。缓存策略可以精细控制带hash的JS/CSS可以缓存30天页面本身不缓存。1.3 为什么专门聊Windows下的部署很多文章默认你是Linux服务器命令全是systemctl、vi、CentOS那套。但现实里Windows Server撑起中小项目的情况非常多尤其是公司内网系统、毕业设计项目、临时验证环境。Windows下部署Nginx本质上和Linux没有区别——同一个nginx.conf逻辑完全一样。区别只在启动方式、路径写法、排障入口这些外围细节上。所以这篇文章我以Windows为主线讲清楚配置思路你理解了以后就算切到Linux也只是把start nginx换成nginx把路径分隔符从/保留成/而已。2. Windows下Nginx准备安装、启动与三个容易踩的环境细节2.1 下载与目录规范去nginx.org的下载页拿Windows版本。不要从乱七八糟的第三方下载站拉Windows下很多install包会捆绑推广软件。我一般是下载nginx/Windows-x.x.x的zip压缩包。解压后的目录放到纯英文路径比如C:\nginx-1.26.0不要带中文、不要带空格。Nginx在Windows下对路径的处理比较敏感放到D:\软件目录\nginx这种路径后面配置root时容易出现各种莫名其妙的行为。也别图省事直接解压到桌面桌面路径里往往带有用户名如果用户名是中文够你折腾半天。解压完成后的目录结构C:\nginx-1.26.0 ├─ conf # 所有配置文件都在这里 ├─ contrib ├─ docs ├─ html # 默认站点目录 ├─ logs # 日志目录排障第一入口 ├─ temp └─ nginx.exe2.2 启动、停止与重载先背熟这5条命令进入目录打开cmd建议“以管理员身份运行”尤其80端口被占时需要提权按顺序执行cd C:\nginx-1.26.0 # 1. 检查配置语法是否正确 nginx -t # 2. 启动Nginx注意不是直接双击nginx.exe start nginx # 3. 确认进程启动成功 tasklist | findstr nginx # 4. 修改配置后重载 nginx -s reload # 5. 停止 nginx -s stop有个细节很多人不知道启动一定要用start nginx不要直接双击nginx.exe。双击的话cmd窗口会一直挂在前台关掉窗口Nginx就一起退出了。用start会把它放到后台运行cmd窗口关了也不受影响。改完配置文件后我习惯先执行nginx -t确认语法再nginx -s reload。-t如果输出successful说明配置没毛病。reload是平滑重载不会中断现有请求这在线上环境很关键。2.3 Windows专属的三个坑第一个端口占用。Nginx默认监听80端口Windows上经常被IIS、其他Web服务或者某些软件占用。启动后浏览器访问localhost打不开先查端口netstat -ano | findstr :80看到LISTENING的行记住最后一列的PID然后tasklist查一下是什么进程占了。确认不是系统服务后可以改Nginx的监听端口把listen 80改成listen 8080。第二个防火墙和杀毒软件。首次启动时Windows防火墙会弹窗问是否允许Nginx通信一定选“允许”。有些杀毒软件会把nginx.exe当成可疑程序隔离建议把整个nginx目录加白名单。遇到过几次用户反馈“Nginx启动不了”最后发现是杀软把exe给吞了。第三个日志。Nginx启动失败时不是每次都在cmd里报错很多错误只写进logs\error.log。这个文件就是排障的起点打开看最后几行90%的问题能直接定位。比如端口被占日志里会写类似bind() to 0.0.0.0:80 failed。3. 构建Vue项目的部署预判publicPath与路由模式怎么选3.1 publicPath决定资源文件从哪里加载很多白屏问题的根源就是这个配置。vue.config.js里的publicPath控制构建后资源文件的引用路径默认值是/。如果项目部署在http://你的域名/也就是网站根路径publicPath: /完全没问题。如果部署在子路径比如http://你的域名/myapp/就必须设置publicPath: /myapp/否则页面加载时会去http://你的域名/js/xxx.js找资源直接404。还有一种写法是publicPath: ./让资源相对当前页面加载。这个在hash模式下比较稳但在history模式下会因为路由多级跳转导致路径错乱不推荐在生产环境这么干。判断方法很简单build完之后打开dist/index.html看里面script和link标签的src/href是绝对路径还是相对路径是/js/app.js还是./js/app.js就能反推publicPath有没有配对。3.2 路由模式hash与history的取舍Vue Router有两种模式。hash模式下URL长这样http://your-domain/#/detail/1。好处是刷新页面不会404因为#后面的内容不会被发到服务器。坏处是URL不美观部分场景下分享链接会有问题SEO也基本没法做。history模式下URL长这样http://your-domain/detail/1。美观配合服务端配置可以实现真正的“整站部署”。但坏处是用户在/detail/1页面按F5刷新浏览器会向服务器请求/detail/1这个路径Nginx在磁盘上找不到这个文件于是返回404。解决方案就是配置try_files让所有匹配不到的路径都回退到index.html由前端路由自己接管。下面会重点讲。3.3 构建产物验证先别急着上Nginx执行npm run build得到dist目录。我每次构建完会先做一次“最小验证”cd dist python -m http.server 8000然后浏览器访问http://localhost:8000。如果这个能正常打开说明产物本身没问题问题只出在配置上。如果这一步就白屏先回去查publicPath或者检查有没有“独立引用的动态资源路径写死”的问题别急着怪Nginx。这一步能帮你把问题范围缩小一半排查时极其重要。4. 手动写一份动静分离配置Nginx conf逐行拆解4.1 一份可以直接抄的完整配置下面这份配置是我在Windows环境跑通后精简出来的包含了动静分离最核心的部分。把它替换掉conf\nginx.conf里的http {}块内容即可。worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; # 开启gzip压缩对文本类资源收益最大 gzip on; gzip_min_length 1k; gzip_types text/plain application/javascript text/css application/json image/svgxml; server { listen 80; server_name localhost; # Vue项目打包后的静态文件根目录 root C:/nginx-1.26.0/html/dist; index index.html; # 动静分离核心静态与动态请求分别处理 # 1. 页面与前端路由所有非文件请求回退到index.html location / { try_files $uri $uri/ /index.html; } # 2. 动态接口转发给后端服务 location /api/ { proxy_pass http://127.0.0.1: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; } # 3. 静态资源带hash的文件走长效缓存 location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?)$ { expires 30d; add_header Cache-Control public, immutable; } } }4.2 逐行拆解try_files到底做了什么先看location /里的try_files $uri $uri/ /index.html。这一行的作用链是用户访问/Nginx去root目录找index.html找到直接返回。用户访问/detail/1浏览器会向服务器要这个路径。Nginx先去磁盘找dist/detail/1这个文件找不到。然后尝试$uri/即dist/detail/1/这个目录还是不存在。最终回退到/index.html把整个前端页面返回给浏览器。Vue Router接管路由展示对应页面。这就是history模式不404的关键。如果新项目还在用hash模式这行配不配都行但我强烈建议从开始就用history模式配合这行配置整个项目从开发到上线都是一种舒服的体验。4.3 动态请求转发proxy_pass带不带斜杠结果完全不同这是整个动静分离配置里最容易被低估的坑。看两个写法# 写法A不带尾部斜杠 location /api/ { proxy_pass http://127.0.0.1:8080; } # 写法B带尾部斜杠 location /api/ { proxy_pass http://127.0.0.1:8080/; }区别在于请求转发到后端时路径是怎么拼的客户端请求写法A转发到后端写法B转发到后端/api/login/api/login/login/api/user/list/api/user/list/user/list写法A是“原样转发”location匹配到的完整路径不动写法B是把location前缀/api/替换成了/。具体用哪种取决于后端接口本身带不带/api前缀后端的Controller类上写了RequestMapping(/api)接口真实路径就是/api/login那用写法A不带斜杠。后端不感知/api或者网关层已经统一处理过前缀那可能要用写法B把/api剥掉。我自己的习惯是前端封装axios时统一加baseURL: /api后端也统一带/api前缀Nginx里用写法A不带斜杠。这样最直观前后端对路径的认知保持一致排查问题时一眼能看懂。4.4 反向代理的三条header建议上面配置里的三行proxy_set_header不是随便写的Host $host把浏览器的Host原样传给后端。有些后端框架根据Host生成重定向地址不传可能跳错域。X-Real-IP $remote_addr后端拿到的真实客户端IP。不配的话后端看到的IP都是127.0.0.1日志和审计全废。X-Forwarded-For $proxy_add_x_forwarded_for追加最原始的客户端IP链路适合有多层代理的场景。如果不配这三行遇到“后端拿不到用户IP”“登录成功但跳转地址错误”这类问题排查半天不如直接补上。4.5 静态资源的长缓存与页面不缓存的组合带hash的静态资源比如app.8f3a2b.js文件内容一变文件名就会变所以可以放心地给它们设置expires 30d浏览器缓存住下次直接读缓存加载速度快得多。但是index.html这个入口文件本身绝对不能设长缓存。因为它是不带hash的每次发布内容都会变。如果浏览器把它缓存住用户打开页面拿到的还是旧版HTML引用的是旧的JS等于前端白发布。所以上面的配置里只有匹配到.js|.css|.png等后缀的资源才加expires 30d而/路径的请求不做任何缓存策略默认走协商缓存。发布新版后HTML及时更新带新hash的资源自然会被浏览器重新请求这种组合在长期维护的项目里非常重要。5. 动与静之间的边界匹配优先级、缓存策略与常见代理细节5.1 location匹配优先级为什么静态和动态没打架Nginx的location匹配不是“从上到下谁先匹配谁生效”它有一套优先级规则记住这个顺序基本不会犯错精确匹配优先级最高。^~前缀匹配一旦命中就不再往下看正则。~/~*正则匹配按出现顺序匹配。普通前缀匹配记录最长匹配项。我在配置里用了两个前缀location/和/api/和一个正则location~*\.(js|css|...)$。/api/是更长的前缀所以接口请求会精确命中它。静态资源请求路径里不包含/api/就会走到正则location里加缓存头。而路由页面请求既不包含/api/也不匹配静态后缀就落到location /的try_files逻辑。三块各司其职核心思想就是用前缀区分“动态接口区域”用后缀正则识别“静态资源文件”剩下全部交给前端路由。5.2 gzip压缩白捡的性能提升浏览器请求时带上Accept-Encoding: gzipNginx发现文件超过gzip_min_length指定的大小就会先压缩再返回。前端项目的JS、CSS、SVG压缩率相当可观经常能减少60%以上的体积。注意两点图片jpg、png、gif本身就压缩过了再压收益不大还费CPU所以gzip_types里不加它们。API返回的JSON也要能压缩application/json一定加上。配置好之后验证方法是在浏览器F12里看请求响应头里有没有Content-Encoding: gzip并且观察nginx.conf里gzip配置块的语法顺序有些老版本对gzip_types有个大坑一旦配置了gzip_types就必须显式包含你需要的所有类型因为这时候它不会再有默认值。5.3 WebSocket与文件上传常规动静分离之外的两种特殊场景如果项目里有WebSocket连接即时聊天、在线协作这类反向代理需要额外加两行location /ws/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; }如果不加浏览器和服务器之间的连接握手会失败具体表现为连接建立后很快断开或者始终连不上。另一个是文件上传的场景Nginx默认允许的请求体大小是1M。做一个图片上传功能稍大一点的图就直接报413需要在http块或server块加上client_max_body_size 20m;这个参数在Windows下没有平台差异按业务实际需要设置就行。5.4 跨域问题动静分离之后为什么反而不需要处理CORS了开发环境配vue.config.js的proxy是因为浏览器和dev server之间本来就不同源需要代理转发。但生产环境用Nginx做反向代理后前端页面和API接口都在同一个域名下页面地址http://your-domain/index.html接口地址http://your-domain/api/login浏览器看它们同一源不存在跨域后端完全不需要配置CORS头。如果你发现生产环境还有跨域报错第一件事是确认接口请求是不是真的走过了Nginx而不是前端代码里写死了其他域名。很多项目在开发时后端顺便加了CrossOrigin或全局CORS配置这点在生产环境是没有问题的但如果在同一个Nginx里做“前后端分开域名部署”那还是需要后端处理跨域注意区分。6. Windows实测部署过程中的疑难记录与排查链路6.1 白屏第一坑资源路径不对现象首页白屏F12里看到JS/CSS资源全部404。排查思路先看资源请求的完整URL。如果请求路径是http://localhost/js/app.js但实际应该从http://localhost/myapp/js/app.js加载那显然publicPath配错了。解决修改vue.config.js中的publicPath: /myapp/重新build。我的排查顺序用静态服务器直接跑dist排除产物本身问题。打开dist/index.html看脚本src路径是否正常。确认Nginx配置里root指向的是不是dist目录。逐步排查不要上来就改配置。这个坑大多数情况下不是Nginx的错而是构建配置与部署路径不匹配。6.2 history路由刷新404现象页面内点击跳转一个页面正常。然后按F5404。原因Nginx收到/detail/1请求后在root目录下找不到同名文件。解决在location /里补上try_files $uri $uri/ /index.html;。这是history模式部署的标配。补充一个变体如果前端部署在子路径比如/myapp/那try_files的最后一项要写成/myapp/index.html否则回退的路径不对刷新仍然会404。前面配置里我写的是/index.html这是因为项目在根路径部署如果你改过前缀一定要同步改这里。6.3 reload不生效的真相现象改了nginx.conf执行nginx -s reload重新访问页面老页面还在。排查跟我念第一句“先nginx -t”。如果语法错误reload不会生效但也不报错cmd里可能一闪而过。然后确认reload到底成没成功tasklist | findstr nginx看nginx的PIDreload成功后主进程的PID会变化。如果没变检查error.log有没有加载失败的信息。还有一个很容易忽略的情况改了Vue代码重新build后浏览器还是老页面。这不是Nginx的问题是浏览器缓存。很多项目JS带hash新build后文件名变了老文件已经不在服务器上但浏览器还在用缓存的HTML引用的旧JS。解决方案是给index.html设置短缓存同时静态资源确保带hash。6.4 代理路径多一层、少一层现象接口请求404或者在F12里看到路径不对劲。排查链路先看浏览器请求的URL是/api/login还是其他。用curl http://localhost/api/login -v直接测Nginx代理看返回什么。确认后端服务在本机是否正常直接访问http://127.0.0.1:8080/api/login看通不通。再对照proxy_pass带不带斜杠的那套规则验证。一次项目中前端请求的是/api/auth/login后端Controller定义的是/auth/loginNginx配了location /api/ { proxy_pass http://127.0.0.1:8080/; }正好把/api剥掉完美匹配。这种设计需要前后端对命名有共识否则很容易出现路径拼接混乱。6.5 Windows特有路径分隔符与权限问题现象nginx -t报错open() C:/nginx-1.26.0/html/dist failed或者页面访问403。原因与解决Nginx配置文件里路径一律用/即使Windows环境下也写成C:/nginx-1.26.0/html/dist不要写C:\nginx-1.26.0\html\dist反斜杠在解析时会被当成转义符。目录权限不足检查dist目录是否允许当前用户读取。放在C盘根目录下的自定义目录有时候权限比较敏感右键–属性–安全把Users的读取权限放出来。不要在root里拼路径时多写一层目录比如dist目录下还有一层dist这个低级错误我犯过两回。6.6 80端口被占如何快速找到元凶现象start nginx后访问localhost没反应nginx -t却显示配置正常。排查步骤netstat -ano | findstr :80看LISTENING那行的PID然后tasklist | findstr PID常见占用者IIS的w3wp.exe、SQL Server的Reporting Service、VMware的Web服务、迅雷这类软件。确认是自己不需要的服务要么关掉要么改Nginx监听端口。如果暂时不想动占用方直接把Nginx改端口做验证listen 8080;然后访问http://localhost:8080能出页面先证明Nginx本身没问题再回头解决端口冲突。6.7 高德地图这些外部JS加载失败和动静分离无关但影响体验有些项目在index.html里通过script标签引入第三方SDK比如高德地图、七牛上传等。如果部署到线上后出现“地图白屏”“上传按钮失效”第一个反应应该是看外部资源加载有没有被缓存、有没有被墙。虽然不能展开说太多但这类资源建议单独建一个location块设置较短的缓存时间或者直接在域名解析层面用国内节点避免长期空跑。这个场景跟动静分离不冲突只是提醒你静态资源共享一个缓存策略的做法在天然需要动态刷新的第三方资源上要注意例外。写到最后的一点实践经验整套配置我一开始也是从网上东拼西凑抄来的真正理解每个字段是干什么的之后才发现所有“诡异”问题的答案都在配置文件里。Windows下用Nginx部署Vue项目表面上是“写配置”本质上是要把构建产物、路由模式、路径拼写、缓存策略、代理规则这五件事想明白。以后再遇到白屏先按“F12看请求路径 → 确认publicPath → 确认root → 确认proxy_pass”这个顺序排查90%的问题能在五分钟内定位。一个很实用的小建议把每个项目的server配置拆成独立文件放到conf/conf.d/目录下然后在nginx.conf里include conf.d/*.conf;。这样多个项目互不干扰新增站点就是复制一个文件改几下改动时也不会碰主配置。我自己管理一台Windows测试机上五个前端项目靠的就是这个办法省心不少。
返回列表