vue-router 源碼:前端路由

在學習 vue-router 的代碼之前,先來簡單瞭解一下前端路由。

前端路由主要有兩種實現方法:

  1. Hash 路由
  2. History 路由

先來看看這兩種方法的實現原理。

接着我們將用它們來簡單實現一個自己的前端路由。

前端路由

Hash 路由

url 的 hash 是以 # 開頭,原本是用來作爲錨點,從而定位到頁面的特定區域。當 hash 改變時,頁面不會因此刷新,瀏覽器也不會向服務器發送請求。

http://www.xxx.com/#/home
複製代碼

同時,hash 改變時,並會觸發相應的 hashchange 事件。所以,hash 很適合被用來做前端路由。當 hash 路由發生了跳轉,便會觸發 hashchange 回調,回調裏可以實現頁面更新的操作,從而達到跳轉頁面的效果。

window.addEventListener('hashchange', function () {
  console.log('render');
});
複製代碼

History 路由

HTML5 規範中提供了 history.pushStatehistory.replaceState 來進行路由控制。通過這兩個方法,可以實現改變 url 且不向服務器發送請求。同時不會像 hash 有一個 #,更加的美觀。但是 History 路由需要服務器的支持,並且需將所有的路由重定向到根頁面。

History 路由的改變不會去觸發某個事件,所以我們需要去考慮如何觸發路由更新後的回調。

有以下兩種方式會改變 url:

  1. 調用 history.pushState 或 history.replaceState;
  2. 點擊瀏覽器的前進與後退。

第一個方式可以封裝一個方法,在調用 pushState(replaceState)後再調用回調。

function push (url) {
  window.history.pushState({}, null, url);
  handleHref();
}

function handleHref () {
  console.log('render');
}
複製代碼

第二個方式,瀏覽器的前進與後退會觸發 popstate 事件。

window.addEventListener('popstate', handleHref);
複製代碼

路由實現

我們通過 <a> 標籤來進行切換路由,通過一個 <div> 標籤來裝載各路由對應的頁面內容。

參考 vue-router 的調用,我們會這麼地調用一個 Router,將路由與對應組件作爲參數傳入:

const router = new Router([
  {
    path: '/',
    component: 'home'
  },
  {
    path: '/book',
    component: 'book'
  },
  {
    path: '/movie',
    component: 'movie'
  }
]);
複製代碼

數組裏是各路由對應的要顯示的內容,接下來就來開始實現這個 Router

Hash 路由實現

Hash 路由 <a> 標籤都需要帶上 #

<div>
  <a href="#/">home</a>
  <a href="#/book">book</a>
  <a href="#/movie">movie</a>
    
  <div id="content"></div>
</div>
複製代碼

Router 的代碼實現如下:

class Router {
  constructor (options) {
    this.routes = {};
    
    this.init();
    
    // 遍歷,綁定視圖更新
    options.forEach(item => {
      this.route(item.path, () => {
      	document.getElementById('content').innerHTML = item.component;
      });
    });
  }
  
  // 綁定監聽事件
  init () {
    window.addEventListener('load', this.updateView.bind(this), false);
    window.addEventListener('hashchange', this.updateView.bind(this), false);
  }
  
  // 更新試圖
  updateView () {
    const currentUrl = window.location.hash.slice(1) || '/';
    this.routes[currentUrl] && this.routes[currentUrl]();
  }
  
  // 將路由與回調函數關聯
  route (path, cb) {
    this.routes[path] = cb;
  }
}
複製代碼

實現效果如下:

 

hash

 

 

History 路由實現

History 路由需要服務器的支持,可以點擊 這裏 的代碼參考。

<div>
  <a href="javascript:void(0);" data-href="/">home</a>
  <a href="javascript:void(0);" data-href="/book">book</a>
  <a href="javascript:void(0);" data-href="/movie">movie</a>
    
  <div id="content"></div>
</div>
複製代碼

Router 的代碼實現如下:

class Router {
  constructor (options) {
    this.routes = {};

    this.init();
    this.bindEvent();

    // 遍歷,綁定視圖更新
    options.forEach(item => {
      this.route(item.path, () => {
        document.getElementById('content').innerHTML = item.component;
      });
    });
  }

  // 綁定點擊事件
  bindEvent () {
    const _this = this;
    const links = document.getElementsByTagName('a');

    [].forEach.call(links, link => {
      link.addEventListener('click', function () {
        const url = this.getAttribute('data-href');
        _this.push(url);
      });
    });
  }

  // 綁定監聽事件
  init () {
    window.addEventListener('load', this.updateView.bind(this), false);
    window.addEventListener('popstate', this.updateView.bind(this), false);
  }

  push (url) {
    window.history.pushState({}, null, url);
    this.updateView();
  }

  // 更新試圖
  updateView () {
    const currentUrl = window.location.pathname || '/';
    this.routes[currentUrl] && this.routes[currentUrl]();
  }

  // 將路由與回調函數關聯
  route (path, cb) {
    this.routes[path] = cb;
  }
}
複製代碼

實現效果如下:

 

history

 

 

最後

前端路由實現方式有兩種,分別是:

  1. Hash 路由
  2. History 路由

原理都是修改 url 的同時不刷新頁面,不向服務器發送請求,通過監聽特殊的事件來更新頁面。

以上實現全部源碼參考 這裏


作者:cobish
鏈接:https://juejin.im/post/5b330142e51d4558b10a9cc5
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章