前文
本文按2022年2月8日16:58:21来看,如果你没有一定的逆向基础和框架源码的阅读基础,可能无法看懂,可以跳过本章继续向后学
我们本节课目标监听vue的路由跳转改变,地址是
https://www.bilibili.com/video/BV16z4y1o7Mr/?spm_id_from=333.788.recommend_more_video.1
Vue路由分类
古早的前端是直接跳转页面,由后端进行返回
而今晚【强行造词】的前端是在前端来控制路由的跳转,仅由后端在第一次访问的时候返回页面
之后的路由跳转全部托付给了浏览器
Vue的路由是由Vue-router来进行控制的
分为三种
history、hash、abstract模式
history就像
https://www.bilibili.com/video/BV16z4y1o7Mr/?spm_id_from=333.788.recommend_more_video.1
这种,是不带用#符号的,叫历史路由
而如果是hash模式,是带有#符号的
地址如http://localhost:8082/#/
而abstract模式普通开发还不怎么常用,以后我们再跟大家聊
问题
问题来了,我们如果想监听地址的改变,到底应该怎么做呢?
目前来说有两种方法,第一种就是根据hash模式以及history模式的路由跳转进行相应的监听
如history本质上是使用history的pushstate来进行地址跳转的
我们可以参考mdn
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
这样修改的地址只是显示在浏览器和历史记录中,并不会影响页面刷新
而hash的路由改变主要是通过addeventlistener对popstate
或者hashchange
进行监听来达到的
因为对其进行监听还是很简单,我们牛逼闲着蛋疼,所以我们主要玩注入!
首先观察vue-router官方手册
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E8%A7%A3%E6%9E%90%E5%AE%88%E5%8D%AB
我们秒上去一个后置钩子
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
我们观察vue-router源码
https://github.com/vuejs/vue-router/blob/dev/dist/vue-router.js
VueRouter.prototype.afterEach = function afterEach (fn) {
return registerHook(this.afterHooks, fn)
};
然后看registerHook
function registerHook (list, fn) {
list.push(fn);
return function () {
var i = list.indexOf(fn);
if (i > -1) { list.splice(i, 1); }
}
}
大概阅读一下,可以看到是对list插入一个号桉树
而afterEach插入的是afterHooks数组
我们搜索afterHook数组
翻阅源代码,找到是在vue-router实例初始化的时候拿到的
var VueRouter = function VueRouter (options) {
if ( options === void 0 ) options = {};
{
warn(this instanceof VueRouter, "Router must be called with the new operator.");
}
this.app = null;
this.apps = [];
this.options = options;
this.beforeHooks = [];
this.resolveHooks = [];
this.afterHooks = [];
this.matcher = createMatcher(options.routes || [], this);
var mode = options.mode || 'hash';
this.fallback =
mode === 'history' && !supportsPushState && options.fallback !== false;
if (this.fallback) {
mode = 'hash';
}
if (!inBrowser) {
mode = 'abstract';
}
this.mode = mode;
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base);
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback);
break
case 'abstract':
this.history = new AbstractHistory(this, options.base);
break
default:
{
assert(false, ("invalid mode: " + mode));
}
}
};
我们可以找到下方代码的最后一行有
this.app = null;
this.apps = [];
this.options = options;
this.beforeHooks = [];
this.resolveHooks = [];
this.afterHooks = [];
那我们的目标非常简单,找到vue-router实例,对其afterHook投入一个函数,目标就搞定了,那我们该怎么找到vue-router实例呢
这里可能需要大量的学习,所以这里我直接给出答案了
vue-router源码在
Object.defineProperty(Vue.prototype, '$router', {
get: function get () { return this._routerRoot._router }
});
对vue的原型定义了一个$router,返回了vue-router实例,任何的vue实例都可以访问这个对象
那我们该怎么拿到vue实例呢?通过__vue__
可以参考
https://bbs.tampermonkey.net.cn/thread-1425-1-1.html
https://bbs.tampermonkey.net.cn/thread-1438-1-1.html
一般页面的#app是一个根路径,通常存在__vue__
属性
所以我们直接
document.querySelector('#app').__vue__.$router.afterHooks.push(()=>{console.log('路由发生改变')})
通过vue拿到vue实例,通过$router拿到vue路由对象,然后找到全局后置路由钩子,投入一个函数来进行监听
可以看到完成!
结语
世俗总要男人无惧无畏