共计 2870 个字符,预计需要花费 8 分钟才能阅读完成。
需求
调用其它团队流量站埋点统计时会截取锚点 #,导致单页应用分页面流量分析获取不到数据。而本项目迫切需要按菜单、特殊功能模块统计流量情况,等不及流量站的团队开发升级版,于是选择改造项目路由为 hostory 模式。
单页应用路由实现思路
SPA,只加载一个 HTML,页面在用户与应用程序交互时动态更新该页。即理论上来说,只需加载一次页面就可以不再请求(首屏耗时过长需按模块 chunk,预加载 css,按需加载 js),当点击其他子页面时只会有相应的 URL 改变而不会重新加载。
这种情况下实现路由的过程分为两部分:
更新 URL 页面不刷新
URL 变化时执行页面替换逻辑
现在主流有 2 种实现方式:
history.pushState 等触发 popstate 事件
location.hash 的变化触发 hashchange 事件
vue-router 中提供了三种方式 HTML5History(判断是否支持)、HashHistory、AbstractHistory(用于 Node 环境,因为不涉及和浏览器地址相关记录关联在一起;整体流程依旧和 HashHistory 是一样的,只是这里通过数组来模拟浏览器历史记录堆栈信息)
默认为 Hash 模式,组件 (components) 映射到路由(routes),registerHook 设置守卫入栈,在每次跳转的时候,递归守卫集合,将触发的守卫进行解析和执行。
vue-router 源码阅读
实现过程 & 踩坑记录
1. Router 传入配置项,设置 mode 为 ’history’
export default new Router({mode: ‘history’, routes})
2. 开发模式下,webpack 热启动配置 history 模式
webpack 属性中 historyApiFallback 默认能将当前找不到的目录重定向到主目录默认 index.html。在 webpack 配置文件的 devServer 配置,将 url 重写到自己配置的目录:
// webpack.dev.conf.js
devServer: {
…
historyApiFallback: {
rewrites: [
{from: /.*/, to: ‘/index.html’}
],
},
…
}
3. 部署预发后,刷新时资源 404
开发过程一切正常,直到编译打包发布。
状况:从域名点击菜单能正常跳转,但刷新当前页会报错 404。(Eg:从 test.com 点击访问 /111 正常,但直接访问 test.com/111 报 404)
原因:在 History mode 下,直接通过地址栏访问 url 会被 http server 直接解析到该文件路径,但是 spa 的路由是虚拟的,并不能直接找到这个 file,所以会 404。解决思路:如果 URL 匹配不到任何静态资源,就跳转到默认的 index.html,让 router 去解析 url,nginx 中需要配置 try_files。
# 当前项目使用了第一级 ’/’ 来区分产品,所以这里匹配 metric、stocktake
# 一般直接 ’location ~ /’ 即可
location ~ ^(/metric|/stocktake) {
try_files $uri $uri/ /index.html;
}
具体配置官方提供了:其它服务器配置
4. 页面不再 404,开始报语法错误
报错如下 DOM 情况:资源情况:
查看 source,发现 index.html 中,静态资源 vendor.dll. 的引入路径有问题,将相对路径 ’./’ 修改为绝对路径 ’/’
5. 页面正常加载,但后端接口被统一重定向
原因:step3 时在 nginx 处配置了 try_files 加上后端接口也是 /metric、/stocktake 开头。于是就不幸地被 nginx 匹配到规则统一作跳转处理了。解决:区分接口与页面。在项目中统一对后端接口加 ’/api/’,nginx 新建规则匹配 ’/api/’ 并作相应跳转。
// 在发请求的公共方法处添加 ’/api/’
function ajax(url, type, params, opt = ”) {
return Q.Promise((resolve, reject) => {
const config = {
method: type === ‘get’ ? ‘get’ : ‘post’,
// TODO: ‘/api/’ 暂时作为 nginx 区分重定向的匹配字段 待与兆华协商统一 不与路由命名重复
url: C.HOST + ‘/api/’ + url,
params: type === ‘get’ ? params : null,
data: configData(type, params, opt)
}
axios(config).then(checkStatus).then(checkCode).then((response) => {
resolve(response)
}).catch((err) => {
console.log(err)
if (err.msg) Notice(err.msg)
reject(err)
})
})
}
nginx 中添加如下匹配规则并重写 url
“`
# 后台三类开头对应请求不同服务器
location ~ ^/api/system/ {
rewrite ^/api/(.*) /$1 break;
proxy_pass http://assets-api-yf.jd.com;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ ^/api/metric/ {
rewrite ^/api/(.*) /$1 break;
proxy_pass http://metrics-api-yf2.jd.com;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ ^/api/stocktake/ {
rewrite ^/api/(.*) /$1 break;
proxy_pass http://meta-api-yf.jd.com;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
“`
6. 切换产品菜单时会刷新
因为使用的是 window.location,替换成 this.$router.push 即可
7. 非法输入路由时,未正常重定向到首页,直接 404
状况:地址栏输入 test.com/ffsdfdsge,显示 ’404 Not Found’,并未重定向到首页。解决:nginx 配置 error_page。
server {
…
error_page 404 /index.html;
…
}
前端小菜鸟一枚,如表述有误,恳请各位大神指正~