好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

详解vue的hash跳转原理

在new vueRouter的时候我们可以传入一个mode属性,他可以接收三个值:hash/history/abstract

hash和history的区别

history的路径更美观一点 比如http://yoursite.com/user/id,history是基于pushState()来完成 URL 跳转而无须重新加载页面。 但是强制刷新还是会有问题(服务端来解决这个问题),所以history模式需要后端人员配合使用。

hash的路径会带有#,比如http://yoursite.com#/user/id

HashHistory

?

class VueRouter{

  constructor(options){

   this .matcher = createMatcher(options.routes || []);

//这里为了讲解hash模式 所以就不进行判断用户传进来的是哪种模式了

   this .history = new HashHistory( this ); //this vue-router的实例

   }

}

源码这里创建了一个基类我们这里和源码统一,这个基类封装了三种模式公用的方法和属性,那么我们在这里创建一个HashHistory和基类History

?

import History from './base'

// hash路由

export default class HashHistory extends History{

  constructor(router){

   super (router); //继承调用父类 等于call

  }

}

// 路由的基类

export default class History {

  constructor(router){

   this .router = router;

  }

}

如果是hash路由,打开网站如果没有hash默认应该添加#/

?

import History from './base' ;

function ensureSlash(){

  if (window.location.hash){

   return

  }

  window.location.hash = '/'

}

export default class HashHistory extends History{

  constructor(router){

   super (router);

   ensureSlash(); // 确保有hash

  }

}

再看一下初始化的逻辑(上面的router.init函数)

?

init(app){

   const history = this .history;

   // 初始化时,应该先拿到当前路径,进行匹配逻辑

 

   // 让路由系统过度到某个路径

   const setupHashListener = ()=> {

    history.setupListener(); // 监听路径变化

   }

   history.transitionTo( // 父类提供方法负责跳转

    history.getCurrentLocation(), // 子类获取对应的路径

    // 跳转成功后注册路径监听,为视图更新做准备

    setupHashListener

   )

}

这里我们要分别实现 transitionTo(基类方法)、 getCurrentLocation 、setupListener

getCurrentLocation实现

?

function getHash(){

  return window.location.hash.slice(1);

}

export default class HashHistory extends History{

  // ...

  getCurrentLocation(){

   return getHash();

  }

}

setupListener实现

?

export default class HashHistory extends History{

  // ...

  setupListener(){

   window.addEventListener( 'hashchange' , ()=> {

    // 根据当前hash值 过度到对应路径

    this .transitionTo(getHash());

   })

  }

}

TransitionTo实现

?

export function createRoute(record, location) { // {path:'/',matched:[record,record]}

  let res = [];

  if (record) { // 如果有记录

   while (record){

    res.unshift(record); // 就将当前记录的父亲放到前面

    record = record.parent

   }

  }

  return {

   ...location,

   matched: res

  }

}

export default class History {

  constructor(router) {

   this .router = router;

   // 根据记录和路径返回对象,稍后会用于router-view的匹配

   this .current = createRoute( null , {

    path: '/'

   })

  }

  // 核心逻辑

  transitionTo(location, onComplete) {

   // 去匹配路径

   let route = this .router.match(location);

   // 相同路径不必过渡

   if (

    location === route.path &&

    route.matched.length === this .current.matched.length){

    return

   }

   //更新路由并且下面会提到改变根实例上的_route属性

   this .updateRoute(route)

   onComplete && onComplete();

  }

}

?

export default class VueRouter{

  // ...

  //做一个代理

  match(location){

   return this .matcher.match(location);

  }

}

macth方法

?

function match(location){ // 稍后根据路径找到对应的记录

  let record = pathMap[location]

  if (record) { // 根据记录创建对应的路由

  //参数:/about/a:{path:xx,component...},path:'/about/a'

   return createRoute(record,{

    path:location

   })

  }

  // 找不到则返回空匹配

  return createRoute( null , {

   path: location

  })

}

我们不难发现路径变化时都会更改current属性,我们可以把current属性变成响应式的,每次current变化刷新视图即可
在install方法中

?

install(Vue) {

  Vue.mixin({ // 给所有组件的生命周期都增加beforeCreate方法

   beforeCreate() {

    if ( this .$options.router) {

    //调用Vue类中双向数据绑定方法

    Vue.util.defineReactive( this , '_route' , this ._router.history.current);

    }

   }

  });

  // $route和$router方法 这两个方法仅仅是vue中最常见的代理 仅仅是为了更加方便

  Object.defineProperty(Vue.prototype, '$route' ,{ // 每个实例都可以获取到$route属性

   get(){

    return this ._routerRoot._route; //上面刚进行双向数据绑定的

   }

  });

  Object.defineProperty(Vue.prototype, '$router' ,{ // 每个实例都可以获取router实例

   get(){

    return this ._routerRoot._router;

   }

  })

  }

切换路由每次初始化时都需要调用更新_route的方法,因为install的时候把_route进行双向数据绑定,刚进来是没有this._router.history.current的,通过发布订阅方式来进行订阅和更新操作;在init方法中增加监听函数

?

history.listen((route) => { // 需要更新_route属性,出入一个函数

  app._route = route

});

?

export default class History {

  constructor(router) {

   // ...

   this .cb = null ;

  }

  listen(cb){

   this .cb = cb; // 注册函数

  }

  updateRoute(route){

   this .current =route;

   this .cb && this .cb(route); // 更新current后 更新_route属性

  }

}

以上就是详解vue的hash跳转原理的详细内容,更多关于vue的hash跳转原理的资料请关注服务器之家其它相关文章!

原文链接:https://segmentfault.com/a/1190000039369103

查看更多关于详解vue的hash跳转原理的详细内容...

  阅读:45次