一、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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。