vue-router 源碼:實現一個簡單的 vue-router

前言

經過上篇,咱們知道前端理由的兩種實現方法,Hash 路由與 History 路由,而且用它們分別實現了一個前端路由。html

接下來咱們就將 Vue 與 Hash 路由結合,實現一個很是簡單的 vue-router 吧。前端

開始實現

想象一下,若是本身實現了一個 vue-router,會怎麼去使用呢?參考 vue-router 官方的使用方式,看看 html 的使用:vue

<div id="app">
  <p>
    <router-link to="#/">home</router-link>
    <router-link to="#/book">book</router-link>
    <router-link to="#/movie">movie</router-link>
  </p>
  <router-view></router-view>
</div>
複製代碼

這裏會有 router-linkrouter-view 兩個組件須要咱們來實現。再來看 js 的:git

const Home = { template: '<div>home</div>' };
const Book = { template: '<div>book</div>' };
const Movie = { template: '<div>movie</div>' };

const routes = [
  { path: '/', component: Home },
  { path: '/book', component: Book },
  { path: '/movie', component: Movie }
];

const router = new VueRouter(Vue, {
  routes
});

new Vue({
  el: '#app'
});
複製代碼

這裏會有咱們本身定義的組件 Home、Book 和 Movie,而且有它們各自對應的路由。咱們實現的 VueRouter 跟官方的有些區別,在 VueRouter 被 new 時是將 Vue 做爲參數傳入,而不是注入掛載到根實例下。github

接下來就是 VueRouter 的實現了。vue-router

VueRouter

要怎麼來實現 VueRouter 呢,先提供一下實現的思路:app

  1. 綁定 hashchange 事件,實現前端路由;
  2. 將傳入的路由和組件作一個路由映射,切換哪一個路由便可找到對應的組件顯示;
  3. 須要 new 一個 Vue 實例還作響應式通訊,當路由改變的時候,router-view 會響應更新;
  4. 註冊 router-linkrouter-view 組件。

先建立一個 VueRouter:ui

class VueRouter {
  constructor (Vue, options) {
    this.$options = options;
  }
}
複製代碼

綁定事件

給 VueRouter 添加一個綁定事件的方法,一旦路由發生改變,會觸發 onHashChange 方法。this

constructor (Vue, options) {
  this.init();
}

// 綁定事件
init () {
  window.addEventListener('load', this.onHashChange.bind(this), false);
  window.addEventListener('hashchange', this.onHashChange.bind(this), false);
}
複製代碼

路由映射表

將傳入的 options 設置成一張路由映射表,以便於經過路由查找到對應的組件。spa

constructor (Vue, options) {
  this.$options = options;
  this.routeMap = {};
  this.createRouteMap(this.$options);
}

// 路由映射表
createRouteMap (options) {
  options.routes.forEach(item => {
    this.routeMap[item.path] = item.component;
  });
}
複製代碼

options 之中,路由與組件的關係:

const routes = [
  { path: '/', component: Home },
  { path: '/book', component: Book },
  { path: '/movie', component: Movie }
];
複製代碼

生成的路由映射表:

this.routeMap = {
  '/': Home,
  '/book': Book,
  '/movie': Movie
};
複製代碼

響應

咱們須要 new 一個新的 Vue 實例,將當前路由 current 儲存在其 data 之中,當修改了 current 時,router-view 就會本身去更新視圖。

constructor (Vue, options) {
  this.app = new Vue({
    data: {
      current: '#/'
    }
  });
}

// 獲取當前 hash 串
getHash () {
  return window.location.hash.slice(1) || '/';
}


// 設置當前路徑
onHashChange () {
  this.app.current = this.getHash();
}
複製代碼

只要在 router-view 裏使用到了 this.app.current,一旦更新它,便會更新。

註冊組件

router-link 實際上就是一個 <a> 標籤,點擊它便能觸發 hashchangerouter-view 會實現一個 render 方法,將當前路由對應的組件取出,進行渲染。

constructor (Vue, options) {
  this.initComponent(Vue);
}

// 註冊組件
initComponent (Vue) {
  Vue.component('router-link', {
    props: {
      to: String
    },
    template: '<a :href="to"><slot></slot></a>'
  });

  const _this = this;
  Vue.component('router-view', {
    render (h) {
      var component = _this.routeMap[_this.app.current];
      return h(component);
    }
  });
}
複製代碼

完整代碼

至此,一個簡單的 vue-router 就出來了,所有代碼是這樣的:

class VueRouter {
  constructor (Vue, options) {
    this.$options = options;
    this.routeMap = {};
    this.app = new Vue({
      data: {
        current: '#/'
      }
    });

    this.init();
    this.createRouteMap(this.$options);
    this.initComponent(Vue);
  }

  // 綁定事件
  init () {
    window.addEventListener('load', this.onHashChange.bind(this), false);
    window.addEventListener('hashchange', this.onHashChange.bind(this), false);
  }

  // 路由映射表
  createRouteMap (options) {
    options.routes.forEach(item => {
      this.routeMap[item.path] = item.component;
    });
  }

  // 註冊組件
  initComponent (Vue) {
    Vue.component('router-link', {
      props: {
        to: String
      },
      template: '<a :href="to"><slot></slot></a>'
    });

    const _this = this;
    Vue.component('router-view', {
      render (h) {
        var component = _this.routeMap[_this.app.current];
        return h(component);
      }
    });
  }

  // 獲取當前 hash 串
  getHash () {
    return window.location.hash.slice(1) || '/';
  }

  // 設置當前路徑
  onHashChange () {
    this.app.current = this.getHash();
  }
}
複製代碼

最後

將 Vue 與 Hash 路由結合,監聽了 hashchange 事件,再經過 Vue 的 響應機制組件,便有了上面實現好了一個 vue-router。

所有源碼參考 這裏

相關文章
相關標籤/搜索