一、vue-router的hash和history模式,到底差在哪?
p>做Vue项目时,用vue-router搞路由跳转开发时挺顺,一部署到nginx服务器,刷新页面就404,路由跳转后样式还丢了…这事儿是不是特闹心?其实这和vue-router的路由模式、nginx的请求处理逻辑有关,今天就把这些坑一个个掰扯清楚。
先看hash模式:url里会带个,比如http://xxx.com/#/about,后面的内容是前端路由自己玩的,浏览器发请求给服务器时,只会把前面的部分(也就是http://xxx.com/)传给服务器,所以服务器只需要返回根路径的index.html,前端再解析后的路径渲染页面就行,这种模式下,部署到nginx基本不用改配置,刷新页面也不会404——因为服务器收到的始终是根路径请求。
再看history模式:url是“干净”的,比如http://xxx.com/about,它用的是HTML5的history API(像pushState、replaceState这些),好处是url好看、对SEO友好点(虽然SPA主要靠前端渲染,SEO作用有限,但路径看着正规),但问题也在这:当用户刷新页面,或者直接输入http://xxx.com/about访问时,浏览器会把整个/about路径发给服务器,nginx默认会去对应目录找/about这个文件或文件夹,找不到就返回404——这就是“刷新404”的根源!
nginx为啥“搞不定”history模式的页面刷新?
得先明白单页应用(SPA)的逻辑:整个项目就一个index.html,所有路由切换都是前端用JS动态渲染的,比如用户点导航到/about,是前端把url改成/about,然后加载对应的组件,这时候没发新的HTTP请求,但刷新页面时,浏览器以为要请求/about这个“真实”资源,可nginx服务器里根本没有这个文件,自然就404了。
举个现实例子:你做了个博客,用history模式,路由是/article/123,开发时用vue-cli的devServer,它内置了请求处理逻辑,所以刷新没事,但部署到nginx后,nginx不知道“/article/123”该对应哪个文件,只能返回404。
给nginx加配置,让history模式“听话”
核心思路:让nginx把所有找不到的请求,都转发到index.html,让前端路由自己处理,具体改nginx的配置文件(一般在/etc/nginx/conf.d/下的xxx.conf,不同系统路径可能不同,比如Ubuntu是/etc/nginx/sites-available/default),在server块里加这段配置:
server {
listen 80;
server_name 你的域名或IP;
root /usr/share/nginx/html; # 假设你把dist包放到这个目录
index index.html;
location / {
try_files $uri $uri/ /index.html;
# 逻辑:先找请求的文件($uri),再找目录($uri/),都没有就返回index.html
}
}
解释try_files的逻辑:
比如用户访问/about,nginx会先去/usr/share/nginx/html/about找文件,没有;再找/usr/share/nginx/html/about/这个目录,也没有;最后返回/usr/share/nginx/html/index.html,前端路由拿到/about这个路径,就会渲染对应的页面,刷新404的问题就解决了。
细节提醒:静态资源别被“误伤”
如果项目里有静态资源(比如/static/js、/static/css),得确保nginx能正确返回这些资源,别被try_files拦截,所以可以单独给静态资源配location:
location /static/ {
root /usr/share/nginx/html;
try_files $uri $uri/ =404; # 直接找静态资源,找不到返回404
}
这样请求/static/js/app.js时,nginx会直接去/usr/share/nginx/html/static/js/app.js找文件,不会走到/index.html的逻辑,避免静态资源加载出错。
部署后样式、图片全丢了?检查这两个配置
很多人改完nginx配置,路由不404了,但样式没了、图片裂了,大概率是静态资源路径配错了。
Vue项目里的publicPath配置(Vue CLI项目)
打开vue.config.js,里面有个publicPath(旧版叫baseUrl),如果你的项目部署在域名根路径(比如http://xxx.com/),publicPath设为就行,但如果部署在子路径下,比如http://xxx.com/myapp/,那publicPath得设为'/myapp/'——这样打包后,静态资源的路径会是/myapp/static/js/xxx.js,nginx里root对应的目录也要和子路径匹配。
举个配置示例:
// vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/myapp/' : '/',
// 其他配置...
}
nginx里的root和location匹配
假设你把dist包放到/usr/share/nginx/html/myapp目录下,nginx配置得改成这样:
server {
listen 80;
server_name 你的域名或IP;
root /usr/share/nginx/html; # 注意:这里是myapp的父目录
index index.html;
location /myapp/ {
try_files $uri $uri/ /myapp/index.html;
# 因为root是/usr/share/nginx/html,所以请求/myapp/about时,实际找的是/usr/share/nginx/html/myapp/about
}
location /myapp/static/ {
root /usr/share/nginx/html;
try_files $uri $uri/ =404;
}
}
这样静态资源的路径才会对应到/myapp/static/下面,样式和图片就能正常加载了。
hash模式部署时,nginx需要特殊处理吗?
如果用hash模式,url长这样:http://xxx.com/#/about,这时候浏览器发请求给服务器的只有http://xxx.com/,所以nginx不需要配try_files那套——因为服务器永远只收到根路径请求,返回index.html后前端自己解析后的路由。
但如果你的项目部署在子路径(比如/myapp),还是得注意publicPath和nginx的root配置(原理和上面一样)——确保静态资源路径能被正确找到。
部署要踩的坑和解决步骤
-
选路由模式:
- 想要url干净选
history,但得配nginx; - 图省事选
hash,部署时不用改nginx(但url带)。
- 想要url干净选
-
history模式必配nginx的
try_files:把所有请求“兜底”到index.html,让前端路由接管。 -
静态资源路径别踩坑:Vue的
publicPath和nginx的root、location要对应,尤其是子路径部署时。 -
测试环节:部署后多刷新不同路由页面,检查样式、图片、接口是否正常;修改nginx配置后,记得重启nginx(比如
sudo systemctl restart nginx)让配置生效。
其实理解了SPA的“单页”本质——所有路由由前端控制,而nginx是服务器端的请求转发,就明白为啥要让nginx把“不认识”的请求都丢给index.html,把这些逻辑理顺,部署时就不会抓瞎啦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


