1. 程式人生 > 實用技巧 >Router的實現原理

Router的實現原理

Router

單頁面應用(SPA)需配置Router來解決頁面跳轉,Vue、React等框架都支援Router配置,路由常分為hash、history兩種模式。hash模式URL尾部帶有“#”,history模式不帶“#”。history模式需要配合伺服器的URL重寫,否則將出現404。下面使用原生JS實現Router。

路由原理

  1. Router 註冊
    1.1 提供註冊的URL
    1.2 提供回撥函式
  2. URL 跳轉模式
    2.1 push 跳轉
    2.2 replace 跳轉
  3. Router 監聽
    3.1 hashRouter 監聽
    3.2 historyRouter 監聽
  4. Router 模式
    4.1 hashRouter
    4.2 historyRouter

路由實現

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Router</title>
</head>
<body>
    <!-- push 跳轉模式 -->
    <ul>
        <li onclick="Router.push(baseUrl + '')">首頁</li>
        <li onclick="Router.push(baseUrl + 'news')">新聞</li>
        <li onclick="Router.push(baseUrl + 'products')">產品</li>
    </ul>
    <!-- replace 跳轉模式 -->
    <ul>
        <li onclick="Router.replace(baseUrl + '')">首頁</li>
        <li onclick="Router.replace(baseUrl + 'news')">新聞</li>
        <li onclick="Router.replace(baseUrl + 'products')">產品</li>
    </ul>
    <div id="app">

    </div>

<script>
    var app = document.getElementById("app")
    var baseUrl = "/"      // 根目錄

    function RouterClass(options) {
        this.routerMap = {} // 用於路由註冊
        this.curUrl = ""    // 當前的url
        this.mode = -1
        this.modeWhiteList = {
            "hash":this.eventHashRouter.bind(this),
            "history":this.eventHistoryRouter.bind(this)
        }

        Object.keys(this.modeWhiteList).forEach((key,index)=>{
            if(key === options.mode){
                this.mode = index
                this.modeWhiteList[key]()
            }
        })

        // 預設為hash模式
        if(this.mode === -1){
            console.log("default")
            this.mode = 0;
            this.modeWhiteList['hash']();
        }
    }

    // hash 路由
    RouterClass.prototype.hashRouter = function(){
        this.curUrl = window.location.hash.slice(1) || "/"
        this.routerMap[this.curUrl]()
    }

    // history 路由
    RouterClass.prototype.historyRouter = function(){
        this.curUrl = window.location.pathname
        console.log(this.curUrl)
        this.routerMap[this.curUrl]()
    }


    /* 事件監聽 */

    // hash 模式監聽
    RouterClass.prototype.eventHashRouter = function(){
        window.addEventListener("hashchange",this.hashRouter.bind(this))
        window.addEventListener("load",this.hashRouter.bind(this))
    }

    // history 模式監聽
    RouterClass.prototype.eventHistoryRouter = function(){
        window.addEventListener("popstate",this.historyRouter.bind(this))
    }

    /* 路由註冊 */
    RouterClass.prototype.register = function(path,callback){
        this.routerMap[path] = callback || function(){}
    }

    /* 跳轉模式 */

    // push 模式跳轉
    RouterClass.prototype.push = function(url){
        if(this.mode === Object.keys(this.modeWhiteList).indexOf("history")){
            window.history.pushState({},"",url)
            this.routerMap[url]()
        }else{
            url = "#" + url;
            window.location.href = url;
        }
    }

    // replace 模式跳轉
    RouterClass.prototype.replace = function (url) {
        if(this.mode === Object.keys(this.modeWhiteList).indexOf("history")){
            window.history.replaceState({},"",url)
            this.routerMap[url]()
        }else{
            url = "#" + url;
            window.location.replace = url;
        }
    }


    var Router = new RouterClass(
        {mode:"history"}
    );

    Router.register(baseUrl + "",function () {
        app.innerHTML = "index"
    })

    Router.register(baseUrl + "news",function () {
        app.innerHTML = "news"
    })

    Router.register(baseUrl + "products",function () {
        app.innerHTML = "products"
    })
</script>

</body>
</html>