javascript基礎修煉(6)——前端路由的基本原理

【造輪子】是筆者學習和理解一些較複雜的代碼結構時的經常使用方法,它很慢,可是效果卻賽過你讀十幾篇相關的文章。爲已知的API方法自行編寫實現,遇到本身沒法復現的部分再有針對性地去查資料,最後當你再去學習官方代碼的時候,就會明白這樣作的價值,總有一天,你也將有能力寫出大師級的代碼。html

一. 前端路由

現代前端開發中最流行的頁面模型,莫過於SPA單頁應用架構。單頁面應用指的是應用只有一個主頁面,經過動態替換DOM內容並同步修改url地址,來模擬多頁應用的效果,切換頁面的功能直接由前臺腳原本完成,而不是由後端渲染完畢後前端只負責顯示。前端三駕馬車Angular,Vue,React均基於此模型來運行的。SPA可以以模擬多頁面應用的效果,歸功於其前端路由機制前端

前端路由,顧名思義就是一個前端不一樣頁面的狀態管理器,能夠不向後臺發送請求而直接經過前端技術實現多個頁面的效果。angularjs中的ui-router,vue中的vue-router,以及react的react-router均是對這種功能的具體實現。vue

既然前端路由這麼牛逼,那必須的好好研究一下。html5

二. 兩種實現方式及其原理

常見的路由插件中兩種方式都是支持且能夠切換的,例如angularjs1.x中就能夠經過如下代碼從Hash模式切換到H5模式:
$locationProvider.html5Mode(true);
切換到HTML5的路由模式,主要用於避免url地址中包含#而引起的問題。react

1.HashChange

1.1 原理

HTML頁面中經過錨點定位原理可進行無刷新跳轉,觸發後url地址中會多出# + 'XXX'的部分,同時在全局的window對象上觸發hashChange事件,這樣在頁面錨點哈希改變爲某個預設值的時候,經過代碼觸發對應的頁面DOM改變,就能夠實現基本的路由了,基於錨點哈希的路由比較直觀,也是通常前端路由插件中最經常使用的方式。angularjs

1.2 應用

下面經過一個實例看一下,當點擊angularjs的鏈接時,能夠看到控制檯打印出了相應的信息。

ajax

2.HTML5 HistoryAPI

2.1 原理

HTML5History API爲瀏覽器的全局history對象增長的擴展方法。通常用來解決ajax請求沒法經過回退按鈕回到請求前狀態的問題vue-router

在HTML4中,已經支持window.history對象來控制頁面歷史記錄跳轉,經常使用的方法包括:後端

  • history.forward(); //在歷史記錄中前進一步
  • history.back(); //在歷史記錄中後退一步
  • history.go(n): //在歷史記錄中跳轉n步驟,n=0爲刷新本頁,n=-1爲後退一頁。

在HTML5中,window.history對象獲得了擴展,新增的API包括:api

  • history.pushState(data[,title][,url]);//向歷史記錄中追加一條記錄
  • history.replaceState(data[,title][,url]);//替換當前頁在歷史記錄中的信息。
  • history.state;//是一個屬性,能夠獲得當前頁的state信息。
  • window.onpopstate;//是一個事件,在點擊瀏覽器後退按鈕或js調用forward()、back()、go()時觸發。監聽函數中可傳入一個event對象,event.state即爲經過pushState()或replaceState()方法傳入的data參數。

2.2 應用

瀏覽器訪問一個頁面時,當前地址的狀態信息會被壓入歷史棧,當調用history.pushState()方法向歷史棧中壓入一個新的state後,歷史棧頂部的指針是指向新的state的。能夠將其做用簡單理解爲 僞裝已經修改了url地址並進行了跳轉 ,除非用戶點擊了瀏覽器的前進,回退,或是顯式調用HTML4中的操做歷史棧的方法,不然不會觸發全局的popstate事件。

在下面的示例中,點擊導航按鈕,能夠看到url地址欄發生了變化,且控制檯打印出了響應的信息。

3.hash 和 history API對比

對比 hash路由 History API 路由
url字符串 正常
命名限制 一般只能在同一個document下進行改變 url地址能夠本身來定義,只要是同一個域名下均可以,自由度更大
url地址變動 會改變 能夠改變,也能夠不改變
狀態保存 無內置方法,須要另行保存頁面的狀態信息 將頁面信息壓入歷史棧時能夠附帶自定義的信息
參數傳遞能力 受到url總長度的限制, 將頁面信息壓入歷史棧時能夠附帶自定義的信息
實用性 可直接使用 一般服務端須要修改代碼以配合實現
兼容性 IE8以上 IE10以上

三.親手造一個簡單的前端路由插件

造輪子,不是爲了把它裝在你的車上,而是當你在荒郊野外開車而輪子出了問題時多一種選擇。

接下來就本身動手實現一個前端路由的插件吧~

3.1基於Hash的前端路由插件myHashRouter.js

咱們但願實現的功能是:

  • 1.引入MyHashRouter.js
  • 2.經過when()方法來定義若干不一樣的路由狀態
  • 3.經過init()方法啓動路由功能
  • 4.經過點擊導航實現前端路由切換

首先編寫js骨架,如圖所示:

;(function() {
    function Router() {
        //記錄路由的跳轉歷史
        this.historyStack = [];
        //記錄已註冊的路由信息
        this.registeredRouter = [];
        //路由匹配失敗時跳轉項
        this.otherwiseRouter = {
            path: '/',
            content: 'home page'
        }
    }

    /*
     * 啓動路由功能
     */
    Router.prototype.init = function() {

    }

     /*
     * 綁定window.onhashchange事件的回調函數
     */
    Router.prototype._bindEvents = function() {

    }

    /**
     * 路由註冊方法
     */
    Router.prototype.when = function(path, content) {

    }

    /**
     * 判斷新添加的路由是否已存在
     */
    Router.prototype._hasThisRouter = function(path) {

    }

    /**
     * 路由不存在時的指定地址
     */
    Router.prototype.otherwise = function(path, content) {

    }

    /**
     * 路由跳轉方法,主動調用時可用於跳轉路由
     */
    Router.prototype.go = function(topath) {

    }

    /**
     * 用於將對應路由信息渲染至頁面,實現路由切換
     */
    Router.prototype.render = function (content) {
     
    }

    var router = new Router();

    //將接口暴露至全局
    window.$router = router;
})();

完成了路由插件的編寫後,咱們在demo中引入該庫,而後使用when()方法註冊幾個路由地址,再使用init()方法啓動路由,腳本部分代碼以下:

效果:
運行附件中的router-demo-hash.html,點擊導航按鈕,便可看到url地址欄以及內容區域同步更改。

3.2基於History API的前端路由插件myHistoryRouter.js

因爲History API不支持低於IE10如下版本的瀏覽器(其餘大多數現代瀏覽器基本都支持),因此咱們在init()方法啓動時先進行可用性判斷,基本代碼框架與基於Hash的路由插件一致。每一個方法的實現並不難寫,這裏再也不贅述,筆者本身的代碼實現放在附件myHashRouter.js中,水平有限,僅供參考。

3.3集成說明

爲方便理解,本例中將兩種模式分開編寫,若是是插件庫的開發,能夠模仿ui-router增長一個html5mode()的方法,在init()方法啓動路由時,根據所傳的參數生成不一樣的路由插件的單例,也就是咱們常說的工廠模式來實現便可。

四.後記

  • 造車輪是一個很好的學習方式,雖然本身造的車輪很簡陋,可是對於理解工具的底層原理卻頗有幫助。
  • 本例只是編寫了一個路由工具的基本骨架,真正的路由工具還須要作不少功能擴展,個別功能的複雜度也會很高,例如路徑的正則匹配,懶加載,組合視圖,嵌套視圖,路由動畫等等,有興趣的小夥伴能夠在本例提供的框架上進行學習擴展。
  • 附件說明:
    • index_h5history.html —— history API基本用法演示demo
    • index_hashchange.html —— hashchange基本用法演示demo
    • router-demo-hash.html —— 引用了myHashRouter.js的demo
    • myHashRouter.js —— 本身開發的基於hash簡易路由插件
    • router-demo-hash.html —— 引用了myHashRouter.js的demo
    • myHistoryRouter.js —— 本身開發的基於historyAPI的簡易路由插件
    • router-demo-history.html —— 引用了myHistoryRouter.js的demo
相關文章
相關標籤/搜索