前端vue的路由實現原理

前言

路由最初是由後端提出,瀏覽器發出請求,服務器根據路由的配置,返回相應信息。後來隨着ajax的流行,異步數據請求交互在瀏覽器局部刷新。單頁面更是把這種方式用到了極致,不單單是在頁面交互是無刷新的,連頁面跳轉都是無刷新的,本文就從源碼來分析下 Vue路由vue-router的實現原理。vue

1.導入插件

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

經過import導入的插件是一個VueRouter類。node

2.路由註冊

Vue.use(Router)
ajax

經過Vue.use方法對路由作一個註冊,把導入的插件傳進去,和Vue關聯起來。這個方法會調用plugin來執行插件的install方法,因此通常寫插件都會有一個install方法,install方法裏,利用Vue.mixin方法給每個組件注入鉤子函數beforeCreate和destroyed前者得到路由實例,初始化路由配置。同時內置全局RouterView和RouterLink兩個組件。
源碼分析:vue-router

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
      )
    }
  }
複製代碼

...未完待續瀏覽器

相關文章
相關標籤/搜索