当前位置: 首页 > Web前端 > vue.js

vue路由实现(三):hash跳转原理

时间:2023-03-31 22:56:54 vue.js

在新建vueRouter的时候,我们可以传入一个mode属性,可以接收三个值:hash/history/abstracthash和history的区别history的路径比较漂亮,比如如http://yoursite.com/user/id,history是基于pushState()完成URL跳转而无需重新加载页面。但是强制刷新还是会有问题(服务端会解决这个问题),所以需要后台人员使用history模式。hash路径会有#,比如http://yoursite.com#/user/idHashHistoryclassVueRouter{constructor(options){this.matcher=createMatcher(options.routes||[]);//这里解释一下hashmode所以不需要判断用户传入的是哪种模式。this.history=newHashHistory(this);//这个vue-router的一个实例}}这里的源码创建了一个基类。我们这里统一源码。这个基类封装了三种模式的常用方法和属性,所以这里我们创建一个HashHistory和基类HistoryimportHistoryfrom'./base'//hashroutingexportdefaultclassHashHistoryextendsHistory{constructor(router){super(路由器);//继承调用父类等于调用}}//路由的基类exportdefaultclassHistory{constructor(router){this.router=router;}}如果是hash路由,如果没有hash打开网站,应该默认加上#/importHistoryfrom'./base';functionensureSlash(){if(window.location.hash){return}window.location.hash='/'}exportdefaultclassHashHistoryextendsHistory{constructor(router){super(router);确保斜线();//确保有哈希}}再看初始化逻辑(上面的router.init函数)init(app){consthistory=this.history;//初始化时,首先要获取当前路径,并进行匹配逻辑//让路由系统过渡到某条路径constsetupHashListener=()=>{history.setupListener();//监听路径变化}history.transitionTo(//父类提供了负责跳转的方法history.getCurrentLocation(),//子类获取对应的路径//跳转成功后,注册路径监听器,为准备视图更新setupHashListener)}这里我们需要实现transitionTo(baseclassmethod),getCurrentLocation,setupListenergetCurrentLocationimplementfunctiongetHash(){returnwindow.location.hash.slice(1);}exportdefaultclassHashHistoryextendsHistory{//...getCurrentLocation(){返回getHash();}}setupListenerimplementsexportdefaultclassHashHistoryextendsHistory{//...setupListener(){window.addEventListener('hashchange',()=>{//根据当前hash值跳转到对应路径this.transitionTo(getHash());})}}TransitionTo实现导出函数createRoute(record,location){//{path:'/',matched:[record,record]}letres=[];if(record){//如果有的话记录while(record){res.unshift(record);//将当前记录的父记录放在前面record=record.parent}}return{...location,matched:res}}exportdefaultclassHistory{constructor(router){this.router=router;//根据记录和路径返回对象,后面用于匹配router-viewthis.current=createRoute(null,{path:'/'})}//核心逻辑transitionTo(location,onComplete){//匹配路径让route=this.router.match(location);//相同的路径不需要过渡if(location===route.path&&route.matched.length===this.current.matched.length){return}//更新路由,改变_route属性on如下所述的根实例this.updateRoute(route)onComplete&&onComplete();}}exportdefaultclassVueRouter{//...//做一个代理match(location){returnthis.matcher.match(location);}}macthmethodfunctionmatch(location){//后面根据路径找到对应的记录letrecord=pathMap[location]if(record){//根据记录创建对应的路由//参数:/about/a:{path:xx,component...},path:'/about/a'returncreateRoute(record,{path:location})}//如果没有找到,则返回一个空的匹配returncreateRoute(null,{path:location})}不难发现,当前的属性会在路径改变的时候改变变化,我们可以把当前的属性改成Responsive,可以在install方法中每次当前变化的时候刷新视图){if(this.$options.router){//调用Vue类中的双向数据绑定方法Vue.util.defineReactive(this,'_route',this._router.history.current);}}});//$route和$routermethods方法只是vue中最常见的代理只是为了更方便Object.defineProperty(Vue.prototype,'$route',{//每个实例都可以获取$route属性returnthis._routerRoot._route;//上面的双向数据绑定}});Object.defineProperty(Vue.prototype,'$router',{//每个实例都可以获取路由器实例get(){returnthis._routerRoot._router;}})}switch路由每次初始化都需要调用更新_route的方法,因为_route在安装的时候绑定了双向数据,刚进来的时候没有this._router.history.current。发布和订阅更新操作;添加监听函数history.listen((route)=>{//需要更新_route属性,进入和退出函数app._route=route});exportdefaultclassHistory{constructor(router){//...this.cb=null;}listen(cb){this.cb=cb;//注册函数}updateRoute(route){this.current=route;this.cb&&this.cb(路线);//更新current后更新_route属性}}