Skip to content

Latest commit

 

History

History
230 lines (201 loc) · 7.01 KB

2.3.Vue-router基本实现原理.md

File metadata and controls

230 lines (201 loc) · 7.01 KB

vue-router使用示例

vue-router路由原理

在解析源码前,先来了解下前端路由的实现原理。 前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则, 显示相应的页面,并且无须刷新。目前单页面使用的路由就只有两种实现方式:hash 模式和history 模式。 hash 模式:即使用 URL 的 hash 来模拟一个完整的 URL ,于是当 URL 改变时,页面不会重新加载。 history 模式:利用了 HTML5 新特性 history.pushState 来完成 URL 跳转。在浏览器不支持的情况下,vue-router会自动退到 hash 模式。

根据mode确定类型

import { HashHistory } from './history/hash'
import { HTML5History } from './history/html5'
import { AbstractHistory } from './history/abstract'

    // ... more

    constructor(options: RouterOptions = {}) {
        // ... more

        // 默认hash模式
        let mode = options.mode || 'hash'

        // 是否降级处理
        this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false

        // 进行降级处理
        if (this.fallback) {
            mode = 'hash'
        }

        if (!inBrowser) {
            mode = 'abstract'
        }
        this.mode = 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:
                if (process.env.NODE_ENV !== 'production') {
                    assert(false, `invalid mode: ${mode}`)
                }
        }
    }

可以看到,会判断是否支持 history , 然后根据 fallback 来确定是否要降级。然后,根据不同的 mode , 分别实例化不同的 history 。 (HTML5History、HashHistory、AbstractHistory)

├── history // 操作浏览器记录的一系列内容 │ ├── abstract.js // 非浏览器的history │ ├── base.js // 基本的history │ ├── hash.js // hash模式的history │ └── html5.js // html5模式的history

不同模式的history的公共实现

export class History {
  router: Router; //router对象
  base: string;  //基准路径
  current: Route;  //当前的路由
  pending: ?Route;
  cb: (r: Route) => void;  //回调
  ready: boolean; 
  readyCbs: Array<Function>;
  readyErrorCbs: Array<Function>;
  errorCbs: Array<Function>;

  // 子类实现
  +go: (n: number) => void;
  +push: (loc: RawLocation) => void;
  +replace: (loc: RawLocation) => void;
  +ensureURL: (push?: boolean) => void;
  +getCurrentLocation: () => string;

  constructor (router: Router, base: ?string) {
    this.router = router
    this.base = normalizeBase(base) //返回基准路径
    this.current = START //route 设置当前route
    this.pending = null
    this.ready = false
    this.readyCbs = []
    this.readyErrorCbs = []
    this.errorCbs = []
  }

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

  onReady (cb: Function, errorCb: ?Function) {
   //*****
  }

  onError (errorCb: Function) {
    //*****
  }
  
  //路由转化操作
  transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const route = this.router.match(location, this.current) //找到匹配路由
    this.confirmTransition(route, () => { //确认是否转化
      this.updateRoute(route) //更新route
      onComplete && onComplete(route)
      this.ensureURL()

      // fire ready cbs once
      if (!this.ready) {
        this.ready = true
        this.readyCbs.forEach(cb => { cb(route) })
      }
    }, err => {
      if (onAbort) {
        onAbort(err)
      }
      if (err && !this.ready) {
        this.ready = true
        this.readyErrorCbs.forEach(cb => { cb(err) })
      }
    })
  }
  //确认是否转化路由
  confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
    const current = this.current
    const abort = err => {
      if (isError(err)) {
        if (this.errorCbs.length) {
          this.errorCbs.forEach(cb => { cb(err) })
        } else {
          warn(false, 'uncaught error during route navigation:')
          console.error(err)
        }
      }
      onAbort && onAbort(err)
    }
    //判断如果前后是同一个路由,不进行操作
    if (
      isSameRoute(route, current) &&
      route.matched.length === current.matched.length
    ) {
      this.ensureURL()
      return abort()
    }
    //下面是各类钩子函数的处理
    //*********************
    })
  }
 
  //更新路由
  updateRoute (route: Route) {
    const prev = this.current //跳转前路由
    this.current = route //装备跳转路由
    this.cb && this.cb(route) //回调函数,这一步很重要,这个回调函数在index文件中注册,会更新被劫持的数据 _router
    this.router.afterHooks.forEach(hook => {
      hook && hook(route, prev)
    })
  }
}

hash模式的功能实现

 //push方法
  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.transitionTo(location, route => {
      pushHash(route.fullPath)
      onComplete && onComplete(route)
    }, onAbort)
  }
  //replace方法
  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.transitionTo(location, route => {
      replaceHash(route.fullPath)
      onComplete && onComplete(route)
    }, onAbort)
  }

html5模式的功能实现

  //push
   push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      pushState(cleanPath(this.base + route.fullPath)) //保存当前的位置信息,用于返回时候复位
      handleScroll(this.router, route, fromRoute, false)
      onComplete && onComplete(route)
    }, onAbort)
  }

  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      replaceState(cleanPath(this.base + route.fullPath)) //保存当前的位置信息,用于返回时候复位
      handleScroll(this.router, route, fromRoute, false)
      onComplete && onComplete(route)
    }, onAbort)
  }

abstract模式的功能实现

  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.transitionTo(location, route => {
      this.stack = this.stack.slice(0, this.index + 1).concat(route)
      this.index++
      onComplete && onComplete(route)
    }, onAbort)
  }

  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.transitionTo(location, route => {
      this.stack = this.stack.slice(0, this.index).concat(route)
      onComplete && onComplete(route)
    }, onAbort)
  }

小结

相关