方式1,平滑重启
做Vue单页应用开发的同学,肯定遇到过这种情况:本地跑项目时,路由跳转、刷新页面都顺顺当当,一部署到Nginx服务器,刷新页面就突然跳出404错误,明明代码没改,咋部署后就出问题了?这得从Vue Router的工作逻辑和Nginx的请求处理方式说起,今天咱们一步步拆解原因和解决办法。
Vue Router两种路由模式,为啥history模式容易触发404?
先得明白Vue Router有两种路由模式:哈希模式(hash mode)和历史模式(history mode),这俩模式的区别,直接决定了部署后会不会出现404。
哈希模式下,URL长这样:https://xxx.com/#/about
,这里的是锚点标识符,浏览器发请求时,只会把前面的部分(也就是https://xxx.com
)发给服务器,后面的内容是前端自己处理的路由,所以不管刷新哪个哈希路由,服务器只需要返回根路径的index.html
,前端再解析后面的内容渲染页面,自然不会有404。
但历史模式更“好看”,URL变成https://xxx.com/about
这种无的形式,这时候问题就来了:刷新页面时,浏览器会把整个https://xxx.com/about
当作请求路径发给服务器,Nginx作为服务器,默认会去自己配置的静态资源目录里,找有没有叫about
的文件或者about
文件夹里的index
文件,可Vue单页应用的路由是前端JS控制的,服务器里根本没有about
这个物理文件,Nginx找不到资源,就返回404了。
Nginx处理请求的逻辑,为啥默认“不认识”前端路由?
Nginx本质是静态资源服务器,它处理请求的逻辑很直接:收到请求后,根据配置的root
(或alias
)目录,去对应路径下找文件,比如你配置root /usr/share/nginx/html
,请求/about
时,Nginx会去/usr/share/nginx/html
里找about
文件;如果是目录请求(比如/about/
),就找about
文件夹里的index
文件。
但Vue单页应用是“单页”,所有路由逻辑都在index.html
里由JS处理,也就是说,不管用户访问/about
还是/user
,服务器都应该返回index.html
,让前端路由接管后续操作,但Nginx默认没这个“觉悟”,它只认物理文件,所以才会出现刷新404的情况。
三步走,解决Nginx下Vue Router history模式404问题
知道了原因,解决起来就有方向了:让Nginx把找不到的请求,统统导向index.html
,具体分三步操作。
步骤1:确认Vue项目的路由模式配置
先打开Vue项目里的路由配置文件(一般是src/router/index.js
),看是不是用了history模式:
const router = new VueRouter({ mode: 'history', // 这里如果是history模式,才需要Nginx特殊配置 base: process.env.BASE_URL, routes: [...] })
如果是hash
模式(默认就是hash,除非你主动改成history),理论上不会有404问题;要是确实用了history模式,就继续往下走。
步骤2:修改Nginx的server配置块
找到Nginx的配置文件(常见路径是/etc/nginx/conf.d/
下的站点配置,或者/etc/nginx/nginx.conf
里的server
块),重点修改location / {}
部分,加入try_files
指令:
server { listen 80; # 或443(https) server_name 你的域名或服务器IP; root /usr/share/nginx/html; # Vue项目打包后dist目录的绝对路径 location / { try_files $uri $uri/ /index.html; # 核心配置! # 下面是可选的性能优化配置(如gzip、缓存) gzip on; gzip_types text/plain application/javascript text/css; } # 可选:单独处理静态资源,避免被try_files拦截 location /static/ { expires 30d; # 静态资源缓存30天 autoindex off; # 关闭目录浏览 } }
解释下try_files $uri $uri/ /index.html;
:Nginx会按顺序检查这三个“候选”:
$uri
:请求的资源文件(比如访问/about
,就找about
文件);$uri/
:请求的目录(比如访问/about
,就找about
文件夹里的index
文件);- 前两个都找不到,就跳转到
/index.html
,把请求交给前端路由处理。
步骤3:重启Nginx并验证
配置改完后,执行命令重启Nginx(不同系统命令可能不同):
# 方式2:系统服务重启(CentOS/Ubuntu等) systemctl reload nginx
重启后,打开页面测试:刷新不同路由(比如/about
、/user
),看是否还出现404,如果页面正常加载,说明配置生效;要是还报错,优先检查这几点:
root
路径是否正确?比如Vue打包后的dist
文件夹,是否真的传到了/usr/share/nginx/html
;server_name
是否和你访问的域名/IP一致?比如配置的是www.xxx.com
,但你用168.1.1
访问,就会匹配不到;- 有没有其他
location
规则干扰?比如之前配置过其他路径的反向代理,导致请求被拦截。
特殊场景:项目部署在子路径下,咋处理?
有些同学的项目不是部署在域名根路径(比如https://xxx.com/
),而是子路径(比如https://xxx.com/my-app/
),这时候配置更复杂,得同时调整Vue和Nginx的配置。
第一步:Vue项目里配置base
属性
如果是Vue CLI创建的项目,在vue.config.js
里设置publicPath
;如果是直接配置Vue Router,设置base
属性:
// vue.config.js(Vue CLI项目) module.exports = { publicPath: '/my-app/' // 部署的子路径 } // 或者router/index.js(直接配置Vue Router) const router = new VueRouter({ mode: 'history', base: '/my-app/', // 和部署子路径一致 routes: [...] })
第二步:Nginx配置子路径的location
修改Nginx的server
块,针对子路径配置location
,还要注意root
和alias
的区别:
server { ... location /my-app/ { alias /usr/share/nginx/html/my-app/; # 用alias替换整个路径 try_files $uri $uri/ /my-app/index.html; # 回退到子路径下的index.html } }
这里解释下alias
和root
的区别:
root
是“拼接路径”:比如root /usr/share/nginx/html
,请求/my-app/js/app.js
会变成/usr/share/nginx/html/my-app/js/app.js
;alias
是“替换路径”:alias /usr/share/nginx/html/my-app/
,请求/my-app/js/app.js
会直接指向/usr/share/nginx/html/my-app/js/app.js
。
如果用root
,得写成:
location ^~ /my-app/ { # ^~表示优先匹配该location root /usr/share/nginx/html; try_files $uri $uri/ /my-app/index.html; }
两种方式都能实现,但要保证路径对应上,否则静态资源会404。
哈希模式下还遇到404?排查这几点
如果你的项目用的是哈希模式(URL带),理论上刷新不会触发404(因为浏览器只请求前面的根路径),要是还遇到404,大概率是这几个原因:
-
部署路径错误:Nginx的
root
没指向Vue打包后的dist
目录,导致根路径下没有index.html
,比如你把dist
传到了/usr/share/nginx/html/my-app
,但root
配成了/usr/share/nginx/html
,就会找不到文件。 -
服务器缓存干扰:之前配置过history模式,Nginx缓存了旧配置,导致请求被错误处理,这时候重启Nginx(甚至重启服务器),或者清除浏览器缓存再试。
-
域名/端口不匹配:Nginx配置的
server_name
是www.xxx.com
,但你用168.1.1
或者其他域名访问,请求会匹配到其他server
块,导致404。
核心是让Nginx把前端路由请求“导回”index.html
Vue Router的history模式让URL更简洁,但需要服务器配合——因为刷新时浏览器会真实请求URL路径,而Nginx默认只认物理文件,解决思路很清晰:用try_files
指令,把所有找不到的请求都导向index.html
,让前端路由接管。
记住这几个关键步骤:确认路由模式是history、修改Nginx配置加入try_files
、处理子路径部署的特殊情况,把这些细节做好,刷新页面404的问题基本就能解决,要是还碰到奇怪的问题,随时留言,咱们一起排查~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。