vue——vue-router 原理

本文详细解析了vue-router的两种前端实现方式:HashHistory和HTML5History。HashHistory利用URL的hash实现视图更新,通过监听hash变化并结合Vue的响应式系统更新视图。replace方法用于替换栈顶记录。HTML5History则通过History接口的pushState和replaceState方法修改浏览器历史记录栈,实现无hash的路由切换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vue-router 主要有三种模式 hash、history、abstract(服务端使用)

前端实现方式主要有以下两种:

  • hash ---- 利用 URL 中的 hash(“#”,window.location.hash)----(默认方式)
  • 利用 History interface 在 HTML5 中新增的方法 >>详情点击
    在这里插入图片描述
// 根据 mode 确定 history 实际的类并实例化
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:
    if (process.env.NODE_ENV !== 'production') {
      assert(false, `invalid mode: ${mode}`)
    }
}

HashHistory

主要替换路由的方法:HashHistory.push()HashHistory.replace()

push

其中 push 是向栈中去添加一条记录

具体执行步骤:

// 1、调用方法
$router.push() 
// 2、根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)
HashHistory.push() 
// 3、监测更新,更新则调用History.updateRoute()
History.transitionTo() 
// 4、更新路由
History.updateRoute() 
// 5、替换当前app路由
{app._route= route} 
// 6、更新视图
vm.render() 

hash虽然出现在url中,但不会被包括在http请求中,它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面。

可以为hash的改变添加监听事件:

window.addEventListener("hashchange",funcRef,false)

每一次改变 hash(window.location.hash),都会在浏览器访问历史中增加一个记录。
利用 hash 的以上特点,就可以来实现前端路由"更新视图但不重新请求页面"的功能了。

HashHistory.push()
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.transitionTo(location, route => {
    pushHash(route.fullPath)
    onComplete && onComplete(route)
  }, onAbort)
}

function pushHash (path) {
  window.location.hash = path
}

hash的改变会自动添加到浏览器的访问历史记录中。
那么视图的更新是怎么实现的呢,我们来看看父类 History 中的 transitionTo() 方法:

transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const route = this.router.match(location, this.current)
  this.confirmTransition(route, () => {
    this.updateRoute(route)
    ...
  })
}

updateRoute (route: Route) {
  this.cb && this.cb(route)
}

listen (cb: Function) {
  this.cb = cb
}

可以看到,当路由变化时,调用了 Hitory 中的 this.cb 方法,而 this.cb方法是通过 History.listen(cb) 进行设置的,回到 VueRouter 类定义中,找到了在 init() 中对其进行了设置:

init (app: any /* Vue component instance */) {
  this.apps.push(app)
  history.listen(route => {
    this.apps.forEach((app) => {
      app._route = route
    })
  })
}

app 为 Vue 组件实例,但是 Vue 作为渐进式的前端框架,本身的组件定义中应该是没有有关路由内置属性 _route, 如果组件中要有这个属性,应该是在插件加载的地方,即 VueRouterinstall() 方法中混入Vue对象的,install.js 的源码:

export function install (Vue) {
  Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) {
        this._router = this.$options.router
        this._router.init(this)
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      }
      registerInstance(this, this)
    },
  })
}

通过 Vue.mixin() 方法,全局注册一个混合,影响注册之后所有创建的每个Vue实例,该混合在 beforeCreate 钩子中通过 Vue.util.defineReactive() 定义了响应式的 _route 属性。所谓响应式属性,即当 _route 值改变时,会自动调用 Vue 实例的 render() 方法,更新视图。

replace

replace 是将栈顶的一条记录给替换掉

replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.transitionTo(location, route => {
    replaceHash(route.fullPath)
    onComplete && onComplete(route)
  }, onAbort)
}
  
function replaceHash (path) {
  const i = window.location.href.indexOf('#')
  window.location.replace(
    window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
  )
}

可以看出,它与 push() 的实现结构基本相似,不同点它不是直接对 window.location.hash 进行赋值,而是调用 window.location.replace 方法将路由进行替换。

HTML5History

  • History interface是浏览器历史记录栈提供的接口,通过back(), forward(), go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。

  • 从HTML5开始,History interface有进一步修炼:pushState(), replaceState() 这下不仅是读取了,还可以对浏览器历史记录栈进行修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值