ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Windows下Nginx部署Vue项目:动静分离与反向代理配置全攻略

Windows下Nginx部署Vue项目:动静分离与反向代理配置全攻略 Windows下用Nginx部署Vue项目这个事很多人觉得简单无非就是npm run build然后把dist目录扔给Nginx托管。但真上手做的时候刷新404、接口跨域、静态资源加载不出来、改完代码浏览器还是旧页面这些问题一个接一个冒出来尤其是动静分离这块配不好整个部署就白搭。我前前后后帮团队配过不少Windows环境的Nginx今天直接把一套能用的完整方案和一些踩坑记录整理出来手把手带你把这事理顺。1. 部署思路先理解Nginx在Windows下承担的三种角色在动手写配置之前先想清楚Nginx在这一整套部署里到底在干什么。很多人上来就改nginx.conf改完发现哪都不对就是因为没搞明白Nginx同时扮演了三个角色。第一个角色是静态文件服务器。Vue打包出来的dist目录里全是静态资源index.html、js、css、图片字体等Nginx需要把这些文件按路径正确返回给浏览器。这是最基础的也是动静分离里“静”这部分。第二个角色是反向代理服务器。生产环境下前端页面要请求后端接口如果直接把后端服务地址暴露在浏览器里一方面会有跨域问题另一方面也不安全。Nginx会把前端过来的/api请求转发给真正的后端服务浏览器看起来请求的是同一个域名实际是Nginx在中间做了一手转发。这是“动”的部分。第三个角色是缓存控制中心。静态文件可以设置长时间的浏览器缓存入口index.html必须设置为不缓存或协商缓存。通过Nginx的expires指令和Cache-Control响应头同一套配置能对不同资源下发完全不同的缓存策略。我把这个思路讲清楚是想强调一点动静分离不是非得在目录结构上把静态资源和接口物理拆到两台服务器虽然那样理解也没错而是在Nginx这一层把不同类型的请求分流处理各走各的规则。理解了这一层后面写配置就是水到渠成的事。2. 环境准备Windows下Nginx和Vue项目的三个前置事项2.1 装好Nginx并了解Windows版的启动方式Windows版Nginx是一个解压即用的zip包去官网下载稳定版建议用1.24.x系列别追最新稳定压倒一切解压到不含中文和空格的目录比如D:\nginx。目录结构里重点关注两个地方conf\nginx.conf是全局配置文件html目录是默认站点目录。很多人第一次启动Nginx都是直接双击nginx.exe然后发现任务管理器里有好几个nginx.exe进程又不知道怎么看日志出了什么问题一头雾水。我建议在命令行里操作先把当前目录切到Nginx解压目录然后这样启动cd /d D:\nginx start nginx.exestart命令会启动Nginx并立刻返回不会霸占当前命令行窗口。启动后可以用tasklist | findstr nginx确认进程存在然后浏览器访问http://localhost看到Welcome页面就说明装好了。2.2 Vue项目打包前的两个配置检查Vue项目打包用的是npm run build默认生成到项目根目录下的dist文件夹。但在打包之前有两个配置必须要检查不然打包出来的东西部署到Nginx上大概率出问题。第一是vue.config.js里的publicPath。这个值决定打包产物里所有静态资源的引用路径前缀。默认是/意思就是资源路径是/js/app.js这种绝对路径。如果将来你的站点部署在域名根路径比如http://192.168.1.10:8088/那用/没问题。但如果部署在子路径下比如http://192.168.1.10:8088/vue-app/就必须改成相对路径或子路径前缀。我先按最常见的根路径部署来讲publicPath保持/即可。第二是路由的mode。Vue Router默认是hash模式URL长这样http://xxx/#/home带着一个#。这种模式部署后刷新不会出问题因为#后面的内容压根不会发给服务器。但很多人希望URL干净一些把mode改成了historyURL变成http://xxx/home这种问题就来了浏览器直接访问http://xxx/home时Nginx会在磁盘上找home这个文件或目录找不到就返回404。后面我会专门说怎么用try_files解决这事但你现在先记住history模式必须配合Nginx配置否则刷新直接白屏404。2.3 规划端口与目录结构为了避免和电脑上已有的服务冲突我习惯把前端静态站点监听在8088端口后端接口服务监听在8080端口前端构建产物放在D:\vue-app\dist目录。这套规划在生产环境也够用测试环境更是绰绰有余。目录规划有一个细节在Windows上写Nginx的root路径时既可以用正斜杠D:/vue-app/dist也可以写成D:\\vue-app\\dist千万别用单反斜杠会被当作转义字符处理路径直接失效。3. 静态资源部署让Vue页面先跑起来3.1 第一个可用的server块写法打开conf\nginx.conf在http块内部新增一个server块。我先给一份最小可用配置这份配置能让Vue打包产物在根路径下正常跑起来server { listen 8088; server_name localhost; root D:/vue-app/dist; index index.html; location / { try_files $uri $uri/ /index.html; } }这里的关键就在try_files $uri $uri/ /index.html;。它的意思是用户请求某个路径时先看看磁盘上有没有对应文件$uri有就直接返回没有的话再看看有没有对应目录$uri/目录里有index.html就返回它都没有就把/index.html返回给浏览器让Vue Router接管路由。这一行配置就是history模式不404的命根子。没有它你访问http://localhost:8088/homeNginx会去磁盘找D:\vue-app\dist\home这个文件找到才怪直接404。有了它不管用户访问/home还是/about还是/detail/123Nginx一律把index.html丢给浏览器然后前端路由自己判断该渲染哪个页面。把这一步想明白很多“刷新就404”的疑惑瞬间就解开了。3.2 验证配置和启动服务每次改完nginx.conf别急着重启先做一次语法检查nginx -t看到nginx: configuration file ... test is successful这样的输出说明语法没问题然后重载配置nginx -s reloadreload会平滑重载配置不需要停止再启动这个操作在Windows下同样是支持的。如果改了配置但浏览器访问没变化先确认是不是真的reload成功了。nginx -t有报错就仔细看提示绝大多数是少写了分号或者括号没闭合。接着把Vue项目打包npm run build产出dist目录后把内容完整拷贝到D:\vue-app\dist浏览器访问http://localhost:8088看到你的页面就说明静态部署这一环通了。3.3 前端的打包细节与产物检查打包完成后建议打开dist目录看一眼结构正常情况下会有index.html和static或js、css等取决于assetsDir配置目录。打开index.html的文件内容检查里面的资源引用路径是不是/js/app.js这种以斜杠开头的形式。如果打包时publicPath配错了你会看到类似./js/app.js或../js/app.js的相对路径这种在根路径部署下通常也能跑但在子路径场景下就另当别论了。另外如果你用的Vue CLI创建的项目vue.config.js里建议显式指定assetsDir: static这样所有静态资源都会归拢到static目录下和入口index.html分开。这个习惯对我后面讲动静分离的目录匹配非常有帮助你可以把它理解为把不同职责的文件先物理分区后面Nginx配置时就能用路径前缀精准分流。4. 动静分离配置静态资源缓存策略和接口转发一次讲透4.1 动静分离的本质按请求特征分流很多人刚接触“动静分离”这个词时容易觉得很高端其实拆开就是一句话把静态文件请求和动态接口请求分开处理。静态文件走文件系统直接读磁盘返回配合强缓存让浏览器少发请求动态请求走反向代理转发给后端服务处理不设置长缓存保证每次都能拿到最新数据。为什么要分开先说静态资源。Vue打包后的js、css文件通常都带哈希指纹比如app.8f3k2j.js。文件名变了就代表内容变了没变就说明内容没变。针对这类文件我们可以设置一个很长的缓存时间比如30天浏览器第二次访问时直接拿本地缓存连请求都不发加载速度瞬间提升。但index.html不能这么干因为它是整个应用的入口每次部署都要确保浏览器能拿到最新的如果它被缓存了那其它带新指纹的资源也引不进来用户看到的就是旧版页面。再说动态接口。接口返回的数据是实时变化的肯定不能缓存但我们也希望它走Nginx统一转发而不是在前端代码里直接写死后端地址。这既解决了跨域问题也让后端服务的地址不出现在浏览器端算是一层基础防护。4.2 缓存策略落地的两种写法第一种是按目录前缀匹配适合你打包时手动指定了assetsDir: static或者明确把所有静态资源放在某个固定目录的场景。比如我上面提到把Vue构建产物的资源都收拢到static目录后可以这样写location /static/ { alias D:/vue-app/dist/static/; expires 30d; add_header Cache-Control public, max-age2592000; }注意alias的坑location /static/后面如果跟alias那么alias后面的路径要能直接拼接上请求路径中/static/之后的部分。比如请求/static/js/app.jsNginx会去找D:/vue-app/dist/static/js/app.js。如果你用的是root而不是alias那拼接方式就完全不同了root D:/vue-app/dist/static的结果是去找D:/vue-app/dist/static/static/js/app.js多了一层目录直接404。我见过太多人在alias和root上栽跟头核心就一句话root会拼接完整的请求URIalias会把location匹配的部分替换成指定路径。第二种写法是按文件后缀匹配不管文件在哪个目录只要是js、css、图片等静态资源就命中规则location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf)$ { expires 7d; add_header Cache-Control public, max-age604800; }这种正则匹配的场景是如果你不想改Vue的目录结构资源散落在不同路径下用后缀匹配更省事。~*表示不区分大小写的正则匹配\.是转义点号最后的$锚定结尾。后缀匹配的粒度粗一些但胜在省心不用管资源在哪个目录。4.3 动态接口反向代理的写法与斜杠问题接口这一层在server块里加一个location规则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; }这里的核心细节是proxy_pass后面到底带不带斜杠。带和不带转发结果天差地别proxy_pass http://127.0.0.1:8080/;带斜杠请求/api/user/list会被转发成http://127.0.0.1:8080/user/list/api前缀被去掉。proxy_pass http://127.0.0.1:8080;不带斜杠请求/api/user/list会被完整转发成http://127.0.0.1:8080/api/user/list/api前缀原样保留。这要根据你后端接口的实际路径来定。大多数情况后端接口原本就没有/api前缀那前端约定所有接口统一加/api前缀Nginx转发时把前缀剥掉这就得用带斜杠的写法。如果你的后端服务本身就要求带/api路径那就不带斜杠直接透传。这个选择没有对错只看后端到底怎么定义的。然后回到前端开发环境里Vue项目通常用vue.config.js里的devServer.proxy做代理生产环境就把这些代理配置迁移到Nginx。Vue里的请求路径建议写成相对路径比如axios.get(/api/user/info)这样开发环境走Vue代理生产环境走Nginx代理前端代码不用改一行。4.4 动静分离配置好后验证哪些点配置完重启Nginx重点看这几个地方打开浏览器开发者工具切到Network标签刷新页面能看到两类请求。第一类是js、css、图片等静态资源响应头里的Cache-Control如果显示public, max-age...以及Expires字段说明缓存策略生效了。第二类是/api/开头的XHR请求点开看响应头确认是后端服务返回的数据而不是Nginx返回的404或其它错误。再看请求的Proxy头确认真的是通过Nginx转发的路径。如果接口请求报了404优先检查proxy_pass的斜杠问题。如果接口返回了538之类的错误多半是后端服务本身没启动或路径不对这时候直接在服务器本机用curl http://127.0.0.1:8080/user/list测试后端地址是否通能快速缩小排查范围。5. 一份可直接抄作业的完整配置与部署校验清单5.1 完整nginx.conf示例下面这份配置是我在Windows上实际用过的涵盖了静态托管、history路由、动静分离、接口代理和常用优化项可以直接整体替换到http块里的server部分server { listen 8088; server_name localhost; # 开启gzip压缩 gzip on; gzip_min_length 1k; gzip_comp_level 5; gzip_types text/plain text/css application/javascript application/json image/svgxml; gzip_vary on; root D:/vue-app/dist; index index.html; # 入口html不做强缓存保证每次部署后能拿到最新 location /index.html { add_header Cache-Control no-cache, must-revalidate; } # 静态资源按目录匹配长缓存 location /static/ { expires 30d; add_header Cache-Control public, max-age2592000; } # 静态资源按后缀匹配覆盖不在/static/下的资源 location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf)$ { expires 7d; add_header Cache-Control public, max-age604800; } # 接口反向代理 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; proxy_connect_timeout 15s; proxy_read_timeout 60s; } # 前端路由history模式兜底 location / { try_files $uri $uri/ /index.html; } }这份配置里有几处细节值得说。第一个是location /index.html用了精确匹配表示只匹配这个完整路径优先级最高。当浏览器访问/index.html时走这条规则不缓存访问其它静态资源时分别走各自的缓存规则接口请求走代理规则剩下所有路径全部交给try_files兜底。不同请求各找各的出口不会互相干扰。第二个是gzip开启后静态资源传输体积能减少60%~70%尤其js、css这种文本文件效果显著。但要留意gzip_types必须把你要压缩的类型写全默认只压缩text/html不写这三行等于白开了。图片一般不建议开gzip因为图片本身已经是压缩格式再压一遍纯属浪费CPU。5.2 部署后的自检清单Nginx配置搞定、服务跑起来之后别急着收工按这份清单过一遍浏览器访问http://localhost:8088首页正常显示控制台没有红色报错。点击进入一个有二级路由的页面比如/home、/detail/1然后按F5强制刷新页面不404、不白屏。开发者工具Network里js、css请求的响应头Cache-Control已带上max-ageindex.html的响应头Cache-Control是no-cache或未设置长缓存。任意抓一个/api/开头的接口请求确认正常返回数据状态码不是404或502。如果页面里用到了WebSocket比如实时消息记得额外加proxy_http_version 1.1和Upgrade相关头location /ws/的代理规则跟普通/api/不太一样。检查浏览器Network里的请求延迟静态资源应该是毫秒级如果发现某个js文件要好几秒看看是不是没开gzip或缓存没命中。5.3 多项目部署的扩展思路如果你手里的Vue项目不止一个想在同一个Nginx上部署多个单页应用思路也不复杂。可以在http块里加多个server每个server监听不同端口对应一个项目。但更常见的需求是同一个域名下用子路径区分项目比如/app1和/app2分别指向两个Vue应用。这种场景就不能简单用root了得用alias配合try_files调整location ^~ /app1/ { alias D:/vue-app1/dist/; try_files $uri $uri/ /app1/index.html; }这里面有个要注意的地方try_files的最后一项/app1/index.html必须写访问URL对应的路径而不是磁盘文件的绝对路径。原因是try_files的最后一个参数是内部重定向URINginx会重新走一遍location匹配。让/app1/index.html再次命中当前location然后被alias映射到磁盘的D:/vue-app1/dist/index.html。这个逻辑第一次看确实容易绕晕我自己也在这里卡过不少时间建议理解不了就直接抄配置跑一遍眼见为实。6. 常见问题与排查技巧实录6.1 端口被占用怎么处理Windows上最经典的坑就是端口冲突。listen 8088结果启动时Nginx报错或者启动后访问的不是你的页面大概率是这个端口被别的程序占了。排查命令netstat -ano | findstr :8088输出结果里最后一列是占用端口的进程PID然后打开任务管理器查这个PID对应的进程。如果是无关程序要么换端口要么用taskkill /PID 1234 /F强制结束它。我遇到不少次是电脑里装了一些软件自带Nginx或Apache悄悄就把80或8080端口占了用这个命令一查一个准。6.2 修改配置后不生效改完nginx.conf明明执行了nginx -s reload浏览器还是旧效果。先确认nginx -t语法检查通过再确认你重载的是不是同一个Nginx实例。Windows下如果之前双击运行过nginx.exe可能已经在后台驻留了进程而命令行里执行的nginx -s reload找不到对应进程就会报错。这时候最粗暴有效的方法是把所有nginx.exe进程结束干净再重新启动taskkill /IM nginx.exe /F start nginx.exe另外留意一个Windows特有的坑改配置文件时如果文件被其它编辑器占用保存可能失败Nginx读出的是旧内容。改完配置顺手nginx -t一下能通过就说明Nginx读到的是你写的最新版本。6.3 刷新404但首页正常这是最典型的history模式问题。现象访问http://localhost:8088正常点路由跳转也正常一按F5就404。原因前面已经分析过就是Nginx在磁盘上找不到对应的前端路由路径。解决方法就是给location /加上try_files $uri $uri/ /index.html;。如果你加了还不生效检查一下是不是有其它location规则优先级更高把本应走兜底逻辑的请求拦截了。比如某个静态资源匹配规则的范围把home这类路径也吞进去了就会造成干扰。6.4 静态资源404或加载路径不对打开页面发现样式全丢了控制台一堆Failed to load resource这类问题九成是publicPath或root路径配置不对。先用开发者工具看请求的完整URL如果请求的是http://localhost:8088/static/js/app.js但磁盘上文件实际在D:\vue-app\dist\js\app.js那就是打包时assetsDir和Nginx的location路径对不上。要么改打包配置把资源统一放static要么改Nginx的匹配规则跟实际目录一致。这类问题在Windows上尤其容易出现在路径写错的情况下比如大小写不一致、反斜杠误写root和alias用混。6.5 接口404和跨域问题的区别接口返回404先分清楚是Nginx层就找不到后端服务还是后端真返回了404。看响应状态码502/504通常说明Nginx连不上后端检查后端服务是否启动、端口是否对、防火墙是否拦截403说明有权限限制。如果接口直接返回的是后端的业务错误码说明转发已经通了问题在后端自己。跨域请求在部署到Nginx后基本都能解决因为浏览器看到的只有localhost:8088这一个源接口请求也是发到localhost:8088再被Nginx转发不存在跨域。要是发现部署后还是报CORS错误先确认前端代码里请求的地址是不是真的走的是Nginx域名而不是写死了后端IP加端口。6.6 更新部署后用户还是看到旧版本这个和缓存策略强相关。如果index.html被浏览器缓存了部署新版本后用户拿到的还是旧入口文件后面的资源更新全部白搭。我在4.2节里已经把index.html单独设置成了no-cache这是最标准的解法。还有一个辅助手段是给入口HTML加个版本参数比如部署脚本里自动更新index.html中引用的资源链接。不过加了no-cache之后这个辅助手段一般用不上。特别注意no-cache不等于不缓存。它表示浏览器和Nginx之间可以缓存这个文件但每次使用前必须向服务器确认版本是否最新是最新就用缓存里的有新版本就拉新的。这比直接no-store完全不缓存体验更好既保证更新及时又省了重复下载的流量。7. 日常维护的一点建议配置稳定跑起来之后还有几件小事建议顺手做好。Nginx的logs目录下会生成access.log和error.logWindows下日志文件增长很快尤其在频繁调试的时候建议定期清理或者直接用任务计划程序写个定时脚本。我自己习惯在error.log里重点看[error]级别的报错很多配置问题都会在这里留线索排查起来比瞎猜快得多。另外每次改配置之前先备份一份nginx.conf副本标注好日期。这个习惯帮我省过不少事。动静态分离配置一旦复杂起来变量和location规则增多想回滚时没有一个干净的历史版本会非常痛苦。反正Windows下复制一个文件成本极低费不了什么事。最后再交代一下版本选择的问题。Nginx在Windows上的性能表现和生产环境Linux相比确实有一点差距并发高的时候可能不稳定。如果你的项目未来要面临较大流量建议还是优先考虑Linux服务器。但如果是内部系统、中小型项目或者只在Windows环境做演示验证用Windows版Nginx完全够用。我自己现在维护的几个内部系统就是Windows Server跑Nginx跑了大半年没出过问题稳定性和维护便利性都挺满意的。部署这种事没有绝对的最好方案只有最贴合你当前环境的方案。
返回列表