前言
單頁應用(Single Page Application,簡稱SPA)的設計模式在現代Web開發中變得越來越流行,主要因為它能提供類似桌面應用的使用者體驗。SPA透過動態載入和替換部分網頁內容,而無需重新載入整個頁面,這帶來了快速響應和流暢的互動效果。然而,這種模式對傳統的HTTP協議提出了挑戰,因為HTTP本質上是無狀態的,且伺服器無法主動向客戶端推送內容。因此,SPA需要一些技術來管理狀態並實現頁面的動態更新。
正文
使用Hash Router進行管理
在單頁應用中,一個常見的解決方案是使用“Hash Router”。Hash Router利用URL中的hash(#)部分來區分不同的頁面或檢視。當用戶在瀏覽器位址列中修改URL的hash部分時,瀏覽器會觸發hashchange
事件。開發者可以監聽這個事件,並透過非同步請求(如AJAX)從伺服器獲取資料,然後更新DOM以顯示相應的檢視。
有許多小白可能不懂什麼意思
簡單來說,在傳統的多面應用中,每個頁面都有獨特的URL,例如我們從掘金首頁
此時的URL為https://juejin.cn/
,而如果我們進入某一篇文章時,整個頁面會重新載入並重新整理
URL也會隨之改變,使得頁面會重新載入
URL的改變意味著瀏覽器需要重新向對應伺服器傳送請求,十分耗費伺服器資源
但是在單頁面應用當中,頁面的主要內容不會重新載入,而是透過JavaScript動態地替換部分內容,從而提供了更快的響應速度和更流暢的使用者體驗。
這個雜湊路由就是一個錨點
舉個例子!
在某篇文章中,其右邊的目錄就運用了雜湊路由
例如我們點選它的結語,頁面就會跳到這個部分,
並且URL改變為
其URL內的Hash
欄位則會修改,Hash
欄位就是URL中帶#
後的內容
修改後,頁面並不載入,而是跳轉到定義的錨點,這個錨點就是雜湊路由,而在這個URL中,它的結語
的雜湊路由為heading-5
現在你明白什麼是雜湊路由(Hash Router)了嗎
我們現在就來探究一下,這樣一個錨點是如何實現的
先設定三個導航欄
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>手寫Hash Router</title> </head> <body> <nav id="nav"> <ul> <li><a href="#/page1">page1</a></li> <li><a href="#/page2">page2</a></li> <li><a href="#/page3">page3</a></li> </nav> <div id="container"></div> </body> </html>
我們使用<a>
標籤來分別指向三個頁面的URL,#
就代表著URL的雜湊欄位,點選這三個標籤就會分別跳轉這三個錨點。
還定義了一個container
,用來放顯示的介面,就像Vue中的<router-view>
接下來我們透過JS來實現——錨點時空跳躍
也就是說,當我們跳轉至#/page1
時,page1就要放進container中
我們跳轉至#/page1
時,page1就要放進container中
開始動手!
我們先定義一個實現路由類:
class HashRouter{}
再寫一個建構函式,定義一個變數routes
來儲存路由規則,再新增一個事件監聽,監聽hashchange
事件,一旦hash值改變,就呼叫load
方法。
constructor() { this.routes = {}; // 建立一個物件來儲存路由規則 window.addEventListener('hashchange', this.load.bind(this), false); // 當URL的hash部分發生變化時,呼叫load方法 }
我們再寫兩個函式,將各頁面匯入進HTML的方法使用routes
物件透過陣列繫結,也就是註冊路由規則
register (hash, callback = function() {}) { this.routes[hash] = callback; // 註冊路由規則,將hash和對應的回撥函式儲存在routes物件中 } registerIndex(callback = function() {}) { this.routes['index'] = callback; // 註冊預設路由,當hash為空時使用 }
接下來我們來寫load
方法,
先從location.hash
獲取當前URL的雜湊部分。由於雜湊總是以#
開始,所以使用slice(1)
來去除這個字元,得到實際的雜湊值字串。
再根據hash
的值,load
方法會從this.routes
物件中查詢對應的處理器。如果hash
為空,意味著URL中沒有特定的雜湊值,這時會使用預設的index
處理器,即透過registerIndex
方法註冊的回撥函式。如果hash
有值,它會嘗試作為鍵在routes
物件中查詢對應的處理器。
簡單來說,如果URL中有hash
欄位則呼叫該頁面的方法使該頁面展示,如果沒有就展示首頁
load() { let hash = location.hash.slice(1); // 去掉hash字首'#",獲取當前hash值 let handler; if(!hash) { // 如果hash為空,則使用預設路由 handler = this.routes['index']; } else { // 否則根據hash值找到對應的handler handler = this.routes[hash]; } handler && handler.call(this); // 執行找到的handler,如果沒有找到則不執行任何操作 }
最後我們建立一個例項物件,再獲取container
元素
let router = new HashRouter(); // 建立一個HashRouter例項 let container = document.getElementById('container'); // 獲取頁面上的容器元素
我們將三個頁面事件和首頁與例項物件內的routes
物件進行路由規則註冊
// 註冊不同的路由規則 router.registerIndex(() => container.innerHTML = '我是首頁'); router.register('/page1', () => container.innerHTML = '我是Page1'); router.register('/page2', () => container.innerHTML = '我是Page2'); router.register('/page3', () => container.innerHTML = '我是Page3');
最後載入路由規則
router.load(); // 載入當前的路由規則
於是我們的手寫雜湊路由就完成了
點選page1
完整js程式碼
class HashRouter{ constructor() { this.routes = {}; // 建立一個物件來儲存路由規則 window.addEventListener('hashchange', this.load.bind(this), false); // 當URL的hash部分發生變化時,呼叫load方法 } register (hash, callback = function() {}) { this.routes[hash] = callback; // 註冊路由規則,將hash和對應的回撥函式儲存在routes物件中 } registerIndex(callback = function() {}) { this.routes['index'] = callback; // 註冊預設路由,當hash為空時使用 } load() { let hash = location.hash.slice(1); // 去掉hash字首'#",獲取當前hash值 let handler; if(!hash) { // 如果hash為空,則使用預設路由 handler = this.routes['index']; } else { // 否則根據hash值找到對應的handler handler = this.routes[hash]; } handler && handler.call(this); // 執行找到的handler,如果沒有找到則不執行任何操作 } } let router = new HashRouter(); // 建立一個HashRouter例項 let container = document.getElementById('container'); // 獲取頁面上的容器元素 // 註冊不同的路由規則 router.registerIndex(() => container.innerHTML = '我是首頁'); router.register('/page1', () => container.innerHTML = '我是Page1'); router.register('/page2', () => container.innerHTML = '我是Page2'); router.register('/page3', () => container.innerHTML = '我是Page3'); router.load(); // 載入當前的路由規則
總結
雜湊路由是一種實用的技術,它使得SPA能夠在不重新載入整個頁面的情況下實現頁面內容的動態更新,從而提高了應用的效能和使用者體驗。