前端 JS 原生 javascript 和 location.hash 實現一個單頁應用的路由 router

cl_lis發表於2021-04-17

 開篇日常立個flag……

 

前言

最近在做一些應用,類似於單頁應用,想實現類似於 Vue 路由的效果。

但是個人 Vue 基礎四捨五入約等於無,而且看著 Vue-router 吃力+用不起來(因為我的專案前後端不分離,而且使用的 js 語法基本上停留在遠古時代:ES5甚至更久遠以前……)

之前嘗試過模擬,但是模擬太痛苦了,而且一堆問題,還不好維護。

於是想著自己用原生 js 寫一個簡單的單頁應用路由。

 

效果

想實現的效果,無非是頁面內切換不同的元件不會整個頁面重新整理,且操作“前進”、“後退”按鈕頁面也會對應響應。

話不多說,先上效果圖

 

原始碼

gitee:https://gitee.com/chen3322275/singlepagerouter/

 

思路與設定

:思路

1、location.hash 可以修改頁面的錨點

2、當前頁面錨點的改變,會觸發 hashchange 事件

這樣,註冊一個 hashchange 事件,監聽 hash 的變化,切換頁面指定元素的顯示與隱藏,可以達到單頁應用的效果。

:一些設定

首先我們約定一個路由物件 route,route 包含三個屬性,即 id 、title 和 handle。

接著,我們將會設定一個路由表,記錄為 routes,為一個 route 物件的陣列。

最後,實現一個路由器 Router 物件,來監聽以及操作路由跳轉。

三者的定義如下

//route 單個路由物件
var route = { id: 'next', title: '下一頁', handle: function () { console.log("切換到next");}

//routes 路由表
var routes = [
    { id: '' },
    { id: 'index', title: '首頁' },
    { id: 'next', title: '下一頁', handle: function () { console.log("切換到next"); } }
]

//router 路由器
var router = new Router(routes);

route 註釋

屬性 是否必須 說明
id 操作元素的id,也是hash值(即url的#後面的內容)
title 頁面標題,切換路由時一併切換,若沒有,則保留上一路由的標題值
handle 類似於回撥函式,在切換路由時執行

router 說明

方法 是否可用 引數 說明
init() 是,可多次呼叫 routes路由表,defaultRoute初始化後預設路由 初始化路由器,必須給router輸入routes
push() route路由,要切換的路由 切換路由
changPage() 否,不推薦使用 route路由(字串) 不推薦直接使用

注:

1、不要對一個 router 進行多次 new Router()操作 。即 var router = new Router() 時,只 new 一次。(否則會對同一個 router 註冊多次 hashchange 事件)

2、路由器建立以後,可以使用 router.init() 切換路由表

3、基於1和2,new Router 時,可不傳入 routes,但 init() 時,必須傳入不為空的 routes。

4、控制 router 跳轉路由使用 router.push(route) 方法,輸入的 route 可以是路由表的下標,也可以是路由表 id 的字串,也可以是 route 物件,若沒有輸入,則預設不會跳轉。

 

Router 模組 js程式碼

偷一下懶,實現過程就不磨雞了,直接上程式碼

/*
 * 模組:單頁應用路由
 * 作者:lisheng741@qq.com
 * 日期:2021.04.17
 * 使用:var routes = [{ id: '' }, { id: 'index', title: '首頁' }, { id: 'next', handle: function () { console.log("切換到next");} }]
 *       var router = new Router(routes);
 * 注意:不要對一個 router 進行多次 new Router()操作 。若要切換路由表使用 init() 方法即可
 */
function Router(routes, defaultRoute) {
    var othis = this;

    //路由初始化
    routes && othis.init(routes, defaultRoute);

    //繫結 hashchange 事件
    window.addEventListener("hashchange", function() {
        let route = location.hash.slice(1) || "";
        othis.oldRoute == othis.currentRoute;
        othis.currentRoute = route;
        othis.changePage(route);
    });
}

//初始化,可多次初始化
Router.prototype.init = function(routes, defaultRoute) {
    if (routes == undefined || routes.length == undefined || routes.length == 0) {
        console.error("Router初始化失敗:routes錯誤!");
        return;
    }
    this.routes = routes;
    this.currentRoute = location.hash.slice(1) || defaultRoute || routes[0].id; //當前路由獲取順序
    this.oldRoute = "";
    location.hash || history.replaceState(null, null, '#' + this.currentRoute); //hash為空,切換當前hash
    this.changePage(this.currentRoute);
    this.oldRoute = location.hash;
}

//切換路由
Router.prototype.push = function(route, callback) {
    //獲取route
    switch (typeof(route)) {
        case "string":

            break;
        case "number":
            route = this.routes[route] || "";
            break;
        case "object":
            route = route.id || "";
            break;
        case "undefined":
        default:
            route = location.hash.slice(1) || "";
            break;
    }
    location.hash = route; //切換hash,接下來的事情交給hashchange去做。如果與上一次的route一致,不會觸發hashchange事件
}

//切換頁面:route為字串,為空則隱藏所有路由
Router.prototype.changePage = function(route) {
    if (!this.routes || !this.routes.length) { //沒有初始化成功的路由,直接返回
        return;
    }
    for (let i = 0; i < this.routes.length; i++) {
        let e = document.getElementById(routes[i].id);
        if (routes[i].id == route) {
            e && (e.style.display = "block");
            (typeof(routes[i].title) === "string") && routes[i].title && (document.title = routes[i].title);
            (typeof(routes[i].handle) === "function") && routes[i].handle(); //handle 存在,執行函式
        } else {
            e && (e.style.display = "none");
        }
    }
}

  

測試頁面的 Html 程式碼

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>單頁路由測試</title>
</head>
<body>
    <div>
        <h1>Welcome</h1>

        <a href="#">空的</a>
        <a href="#index">index啊啊啊</a>
        <button onclick="router.push('next')">next測試</button>

        <div id="index">
            index啊啊啊
        </div>
        <div id="next">
            next啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
        </div>
    </div>

    <script src="~/js/site.js"></script>
    <script>
        //路由表
        var routes = [
            { id: '' },
            { id: 'index', title: '首頁' },
            { id: 'next', title: '下一頁', handle: function () { console.log("切換到next"); console.log(this); } }
        ]

        //路由器
        var router = new Router(routes, 'index');
    </script>
</body>
</html>

  

 參考來源

魚丸粗麵不要香菜 的 使用js實現單頁應用路由轉跳功能

小蚊 的 用原生js做單頁應用(博文掛掉了,連結複製不到)

相關文章