前端vue的路由实现原理

前言

路由最初是由后端提出,浏览器发出请求,服务器根据路由的配置,返回相应信息。后来随着ajax的流行,异步数据请求交互在浏览器局部刷新。单页面更是把这种方式用到了极致,不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,本文就从源码来分析下 Vue路由vue-router的实现原理。

1.导入插件

    import Router from 'vue-router'
    export default class VueRouter {  
      static install: () => void; 
      static version: string;  
     ...
} 
复制代码

通过import导入的插件是一个VueRouter类。

2.路由注册

Vue.use(Router)

通过Vue.use方法对路由做一个注册,把导入的插件传进去,和Vue关联起来。这个方法会调用plugin来执行插件的install方法,所以一般写插件都会有一个install方法,install方法里,利用Vue.mixin方法给每一个组件注入钩子函数beforeCreate和destroyed前者获得路由实例,初始化路由配置。同时内置全局RouterView和RouterLink两个组件。
源码分析:

export function install (Vue) {

//判断install方法是否调用一次

  if (install.installed && _Vue === Vue) return
  install.installed = true

  _Vue = Vue

  const isDef = v => v !== undefined

  const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      i(vm, callVal)
    }
  }
//给全局组件注入beforeCreate和destoryed钩子函数,Vue实例创建时,会执行
  Vue.mixin({
    beforeCreate () {
    //判断有无router实例
      if (isDef(this.$options.router)) {
        this._routerRoot = this
        this._router = this.$options.router
        //路由初始化,会调用transitionTo做路径切换,
        this._router.init(this)
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })
  
//在原型上创建$router(为一个vueRouter实例,可以调用.push,.go等方法)
//$route(为当前路由跳转对象可以获取name、path、query等)两个属性,
//方便全局调用

  Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })
  
// 全局注册组件 router-link(路由跳转) 和 router-view(路由对应组件内容展示)
  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}
复制代码

3.vueRouter对象

const route = new Router({
 routes: [
   {
     path: '/helloword',redirect: '/welcome' ,
     name: 'HelloWorld',
     component: HelloWorld,
   },
   {
     path: '/home',
     name: 'Home',
     component: Home,
     children: [
       {
         path: 'child1',
         name: 'Child',
         component: Child
       }
     ]
   },
 ]
})
复制代码

new Router时产生这个类的一个实例,路由初始化是在组件初始化阶段,调用beforeCreate钩子函数,执行router.init()初始化路由
源码分析:

var VueRouter = function VueRouter (options) {
  if ( options === void 0 ) options = {};
    ...
  //匹配路由对象
  this.matcher = createMatcher(options.routes || [], this);
//根据mode采用不同的路由方式,默认hash 
//共有三种模式history('/地址',需要服务器设置),hash('#地址'),abstract(非浏览器环境使用)
  var mode = options.mode || 'hash';
  this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false;
  if (this.fallback) {
    mode = 'hash';
  }
  if (!inBrowser) {
    mode = 'abstract';
  }
  this.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:
      {
        assert(false, ("invalid mode: " + mode));
      }
  }
};

init (app: any /* Vue component instance */) {
    process.env.NODE_ENV !== 'production' && assert(
      install.installed,
      `not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
      `before creating root instance.`
    )
    //添加路由实例
    this.apps.push(app)

    // main app already initialized.
    //确保路由多次添加
    if (this.app) {
      return
    }

    this.app = app

    const history = this.history

    if (history instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {
      const setupHashListener = () => {
        history.setupListeners()
      }
      //使用transitionTo完成路径切换
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }
  }
复制代码

...未完待续

你可能感兴趣的:(前端vue的路由实现原理)