1. 概念
路由模式包括 hash模式和history 模式
1.1 hash模式
单页面应用中vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载,只加载需要显示的部分(组件)。hash模式URL带"#",代表瞄点,定位页面加载的显示组件是什么。单页面应用,一个页面显示不同内容就是靠"#",它后面的部分地址代表路由,指向某个组件。如:
http://localhost:7230/#/
http://localhost:7230/#/login
第一个“/”,代表一个路由,指向一个组件,一般是首页。“/login” 代表登录组件路由,指向登录组件。
1.2 history 模式
当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id
,因为我们的应用是个单页客户端应用,用户在浏览器直接访问此地址时会报404错误(F5刷新当前页面们或外部系统调用本系统页面时)。解决这个问题的方法每次请求提交到服务器,服务器找不到资源就返回首页内容index.html,前端收到内容后如果router里面没有该路径,只能匹配404路由
(1)服务器端配置无法匹配资源返回index.html内容
除了index.html资源(首次请求)外,上面地址资源/user/id在服务器是不存在的。因此要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面。nginx配置:
location / {
try_files $uri $uri/ /index.html;
}
(2)前端处理错误路径返回的页面
虽然能解决404问题,但是你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后再给出一个 404 页面。
router.addRoutes([
mainRoutes,
{ path: '*', redirect: { name: '404' } }
])
npm run dev时候没发现这类问题,因为这时候默认给返回了index.html内容。类似实现nginx功能。
如果以上正常但仍然无法访问、空白页、或部分资源404,比如我这里需要用到一个配置文件env.js,但是返回的404,内容返回了空白或者index.html的内容。这本质是由于路径不对造成的,查看是否有使用了相对路径或者错误的路径
<!-- 生产环境 -->
<script>
document.write('<script src="./systemConfig.js?t=' + new Date().getTime() + '"><\/script>');
document.write('<script src="./config/index.js?t=' + new Date().getTime() + '"><\/script>');
// eslint-disable-next-line no-multiple-empty-lines
</script>
<% }else { %>
<!-- 开发环境 -->
<link rel="shortcut icon" type="image/x-icon" href="./static/img/favicon.ico?v=1">
<script src="./static/config/index.js"></script>
<!-- <script src="./static/plugins/mock-1.0.0-beta3/mock-min.js"></script>-->
<% } %>
如以上相对路径引用改为绝对路径即可
2.history 模式改造
考虑到微信不识别#,另外公司领导看到hash模式#不爽,强烈要求去掉,没法办法只能折腾。整个过程大致这样
2.1.前端部分改造
(1) router模式进行修改
const router = new Router({
mode: 'history',
// mode: 'hash',
scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes)
})
(2)刷新页面或者直接输入链接地址空白
发现空白的原因是因为静态资源文件使用相对路径,要改成绝对路径
<title>ERP财务结算</title>
<% if (process.env.NODE_ENV === 'production') { %>
<!-- 生产环境 -->
<script>
document.write('<script src="/systemConfig.js?t=' + new Date().getTime() + '"><\/script>');
document.write('<script src="/config/index.js?t=' + new Date().getTime() + '"><\/script>');
// eslint-disable-next-line no-multiple-empty-lines
</script>
<% }else { %>
<!-- 开发环境 -->
<link rel="shortcut icon" type="image/x-icon" href="/static/img/favicon.ico?v=1">
<script src="/static/config/index.js"></script>
<!-- <script src="./static/plugins/mock-1.0.0-beta3/mock-min.js"></script>-->
<% } %>
</head>
由于是引用了外部js等静态文件,不是import的,所有要用绝对路径
2.2.nginx改造
history模式,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,因此服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面内容
location /{
#文件位置
root /HXG/www/dist-cw;
try_files $uri $uri/ /index.html last;
index index.html;
}
这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后再给出一个 404 页面。
router.addRoutes([
mainRoutes,
{ path: '*', redirect: { name: '404' } }
])
路由对象最后添加404路由,当前面路由都匹配不到时,就执行跳转到404
2.3. 本地调试和nginx服务器执行差异
本地调试使用npm run dev,不管是本地服务器或者服务器调用,都不会访问页面。但是服务器方式,会请求
[数字].js,页面对应的js文件,所以页面对应的js路径一定不能错
2.4.发布部署问题
hash模式发布最简单省事,但是history模式发布遇到复杂一点,就问题多多,例如nginx多层代理(内外网要求),应用没有发布在域名的目录根下如http://xxx.com/wx/,又或如部署到一个子目录。现在总结如下:
(1)直接发布域名下
域名只是绑定这个系统,可以简单配置,参照
server {
listen 1005;
server_name 1005;
location / {
proxy_pass http://101.150.10.23:12345/;
}
}
(2)发布域名下级目录下
有时候存在多个系统挂在同一个域名下,通过不同目录来区分。例如目录是"/cw/"
server {
listen 1004;
server_name 1004;
#页面路径
location /cw/ {
proxy_pass http://101.150.10.23:89/;
}
#静态文件是绝对路径,没有/cw/前缀,因此要设置匹配路由
location ~* \.(js|css|img|png|jpg|ioc)$ {
proxy_pass http://101.150.10.23:89;
}
}
因为js等静态文件是绝对路径,所以需要增加静态文件路由匹配。如果域名下有目录(系统),正则匹配还需要优化,再根据页面路径(referer)规则过滤到不同的系统。以下是存在/cw/和/wl/两个目录:
http://localhost:1006/cw/
http://localhost:1006/wl/
具体参照配置:
server {
listen 1006;
server_name 1006;
####财务系统####
#页面路径
location /cw/ {
proxy_pass http://101.150.10.23:89/;
}
###物流系统####
location /wl/ {
proxy_pass http://101.150.10.23:12345/;
}
#静态文件是绝对路径,没有/cw/和/wl/前缀,因此要设置匹配路由
location ~* \.(js|css|img|png|jpg)$ {
if ($http_referer ~* "http://localhost:1006/cw/")
{
proxy_pass http://101.150.10.23:89;
#break;
}
if ($http_referer ~* "http://localhost:1006/wl/")
{
proxy_pass http://101.150.10.23:12345;
#break;
}
}
}
除了要做nginx配置外,代码层面也需要支持,查阅官方资料如下:
- 如果想部署到一个子目录,你需要使用 Vue CLI 的 publicPath 选项 (opens new window)和
相关的 router base property(基路径)
- 当你在 HTML5 history 模式下使用 base 选项之后,所有的 to 属性都不需要写 (基路径) 了。
这里都提到了基路径base,所以需要用到基路径配置,基路径设置为/cw/
const router = new Router({
mode: 'history',
base: '/cw/',
scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes)
})
基路径有个很重要的特点,请求url路径可以含有基路径,也可以不含有,效果是一样:
http://101.150.10.23:89/cw/about 和 http://101.150.10.23:89/about 访问是一样的
在导航路由中查看路由情况,其实是to不含有基路径的
router.beforeEach((to, from, next) => {
console.log('from===' + from.path)
console.log('to===' + to.path)
}
都是输出
to===/about