浅谈对vueRouter的理解

在前端spa应用中,前端路由占据了举足轻重的地位。改变了浏览器的url地址,不请求服务器资源,自动刷新渲染页面视图。
那么在前端路由中是如何实现的呢?通过vueRouter的实现方式做一个简要的了解。我们都知道在vueRouter中,浏览器下可以通过mode字段来控制路由的模式,而vueRouter将路由模式分为了两个类型:

  • hash模式
  • history模式

首先可以了解一下是如何通过mode字段来实现路由的模式分配的,VueRouter的默认路由模式是hash模式,并且在浏览器不支持history模式(即不支持history.pushState)时会自动降级为hash模式:

export default class VueRouter {
// 只看主要逻辑,目前使用的flow类型检测
  constructor (options: RouterOptions = {}) {
    this.app = null
    this.apps = []
    this.options = options
// mode默认采用hash模式
    let mode = options.mode || 'hash'
    // 回退标志位,是否支持pushState
    this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
    if (this.fallback) {
      mode = 'hash'
    }
    if (!inBrowser) {
      mode = 'abstract'
    }
    this.mode = mode
	// 根据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}`)
        }
    }
  }
}

1. hash模式

现在我们看看hash模式下如何实现的改变url,不请求服务器数据而自动刷新视图。
‘#’ hash符本来在浏览器中的作用是用来表示页面位置的,如我们常用的锚点跳转。

<a href="#box2">锚点2</a>

而在浏览器中改变hash值得时候浏览器会自动将此次的url变化推入导航堆栈中,但是并不会向服务器发送请求。那么此时我们只需要知道hash值变化了,从而触发我们的应用更新对应视图就实现了路由的基础功能了。

监听浏览器中的hash值变化(url手动输入改变hash值)可以通过hashchange事件来做到:

window.addEventListener('hashchange', fn)

那么当在路由中push一次url变化,vueRouter是如何实现通知vue来更新视图的呢?现在看到router中的push方法是怎样定义的:

// index.js
export default class VueRouter {
	// router初始化时写入listen的cb以便更新最新的route数据
	init(app: any /* Vue component instance */) {
		this.apps.push(app)
		const history = this.history
		history.listen(route => {
		  // route数据修改时,对应更新vm实例上的_route数据
      	  this.apps.forEach((app) => {
            app._route = route
          })
        })
	}
	// router上的push方法代理的是history对象上的push方法
	push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
	  // 在调用push方法时如未传递onComplete,onAbort回调函数,且支持Promise则返回一个promise对象
      // $flow-disable-line
      if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
        return new Promise((resolve, reject) => {
          this.history.push(location, resolve, reject)
        })
      } else {
        this.history.push(location, onComplete, onAbort)
      }
  }
}

// hash.js
export class HashHistory extends History {
  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    // 继承自History类的方法
    this.transitionTo(
      location,
      route => {
      	// 修改浏览器上的url值
        pushHash(route.fullPath)
        handleScroll(this.router, route, fromRoute, false)
        onComplete && onComplete(route)
      },
      onAbort
    )
  }
}

function pushHash (path) {
  if (supportsPushState) {
    pushState(getUrl(path))
  } else {
    window.location.hash = path
  }
}

// base.js
export class History {
transitionTo (
    location: RawLocation,
    onComplete?: Function,
    onAbort?: Function
  ) {
    const route = this.router.match(location, this.current)
    // 校验此次跳转是否合法的方法
    this.confirmTransition(
      route,
      () => {
      // 更新current数据,并调用listen中写入的cb函数
        this.updateRoute(route)
        // 调用传入的完成函数,此处为hash.js中push时传入的第二个参数
        onComplete && onComplete(route)
        this.ensureURL()

        // fire ready cbs once
        if (!this.ready) {
          this.ready = true
          this.readyCbs.forEach(cb => {
            cb(route)
          })
        }
      },
      err => {
      // 错误处理回调
      }
    )
  }
}

那么现在可以来看router在hash模式下一次push会发生的过程了:

init -> router.push() -> hashHistory.push() -> History.transitionTo() -> History.updateRoute() -> History.cb() 
// listen时传入的cb,遍历this.apps中每一个vm实例更新_route数据,触发vm的render函数更新视图

这就是一次vueRouter在hash模式下的push过程,现在还有几个疑问:

  1. vue的实例对象vm上为什么会有与vue框架无关的_route对象?
  2. 浏览器上直接修改url如何触发路由更新视图?
  3. 如何触发文件系统加载对应的路由bundle文件?
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值