實現前端路由 (Hash & History)
2 種路由實現方式 & nginx 設定
先總結
前端 router 有兩種實現模式,一種使用 Hash 實現,另一種使用 HTML5 History API 實現。
基於 hash 的路由:。當使用者在應用程式中切換路由(例如從 #/home 切換到 #/article)時,也不會向伺服器發送任何請求,瀏覽器僅根據 hash 的變化來變更頁面內容,。
基於 HTML5 History API 的路由:當使用者首次訪問某個路由(例如 http://example.com/about)時,瀏覽器會向伺服器發送請求,以獲取實際的靜態資源(例如 about.html)。但一旦頁面加載完成並切換到其他路由時(例如從 /about 切換到 /contact),瀏覽器不會再次向伺服器發送請求,而是僅根據 HTML5 History API 的變化來變更 URL 和頁面內容。
相同點:
不同點:
使用 Hash 實現 router
- 原理:。
- 在內部傳遞的實際 URL 之前使用了一個哈希字元(#)。由於 URL 從未傳送到伺服器,所以不需要在伺服器層面上進行任何處理。
<!DOCTYPE html>
<html>
<body>
<div id="content"></div>
<a href="#/">Home</a>
<a href="#/article">Article</a>
<div>window.location.hash =
<span id="currentHash"></span>
<div>
<script>
// 設定路由表映射不同頁面
const routes = {
"/": "Home",
"/article": "Article",
};
// 更新頁面內容 (即更換 <div id="content"></div> 的內容)
function updateContent(route) {
const content = document.getElementById("content");
content.innerHTML = routes[route] || "Page not found";
const currentHash = document.getElementById("currentHash");
currentHash.innerHTML = route;
}
// 監聽 hashchange 事件偵測 window.location.hash 變化,當路由改變觸發 updateContent 更新對應頁面內容
window.addEventListener("hashchange", () => {
updateContent(window.location.hash.slice(1));
});
// 初始加載路由,根據當前 hash 加載入頁面
updateContent(window.location.hash.slice(1));
</script>
</body>
</html>
使用 History API 實現 router
原理:
HTML5 History API 能夠操作瀏覽器歷史記錄(history)。使用此 API 前端路由可以在不刷新整個頁面的情況下改變 URL,並且可以監聽 URL 的變化,以便動態更新應用程式的內容。
切換路由後重整 404 原因:
<!DOCTYPE html>
<html>
<body>
<div id="content"></div>
<a href="/" onclick="navigateTo(event, '/')">Home</a>
<a href="/article" onclick="navigateTo(event, '/article')">Article</a>
<div>Current Path: <span id="currentPath"></span></div>
<script>
// 初始化路由
window.onload = function () {
renderPage(window.location.pathname);
};
// 監聽 popstate 事件,當 URL 變化時渲染不同頁面內容
// 當使用者點擊瀏覽器的後退或前進按鈕時,或使用history.back()、history.forward()、history.go() 等方法時)
window.onpopstate = function (event) {
renderPage(window.location.pathname);
};
// 點擊切換路由,使用 history.pushState 改變 URL
function navigateTo(event, path) {
event.preventDefault();
// history.pushState(state, title, url) 將新的紀錄添加到瀏覽器的歷史堆疊中。不會進行頁面刷新,只改變瀏覽器的 URL,並添加一條新的歷史紀錄。
// 也可以用 replaceState(state, title, url), 該方法不會添加新的歷史紀錄,而是替換當前的歷史紀錄。
history.pushState(null, null, path);
renderPage(path);
}
// 渲染不同的頁面內容
function renderPage(path) {
var content = document.getElementById("content");
var currentPath = document.getElementById("currentPath");
currentPath.textContent = path;
if (path === "/") content.innerHTML = "<h1>Home Page</h1>";
else if (path === "/article") content.innerHTML = "<h1>About Page</h1>";
else content.innerHTML = "<h1>Page Not Found</h1>";
}
</script>
</body>
</html>
/etc/nginx/nginx.conf 配置:(假設使用 GCP 就在 GCE 虛擬機中調整此配置)
- 以下設置將所有路徑轉發到 index.html
server {
listen 80; # 指示 Nginx 監聽 80 port,接受來自這個端口的請求。
server_name hean.tw; # 當有 HTTP 請求通過 80 端口訪問 hean.tw ,這個 Nginx 配置就會被觸發。
root /home/hean/project-name/static; # 靜態文件部署位置,指示伺服器應該從此目錄下提供靜態資源。當用戶訪問 example.com 時,Nginx 會尋找該目錄下的文件來返回。
index index.html; # 指示當用戶訪問根路徑時,Nginx 應該使用 index.html 文件作為默認文件。
# location 處理所有路由,當收到請求先查找與請求的 URI 相對應的文件($uri),如果找不到,則查找與請求 URI 匹配的目錄($uri/),
# 若還是找不到則返回 index.html 文件,相當於由前端路由器來處理路由。確保無論用戶訪問的是應用程式的哪個路徑,Nginx 都會返回 index.html 文件。
location / {
try_files $uri $uri/ /index.html;
}
location /api { # 其他服務如 api
proxy_pass http://localhost:8080;
}
}
- 先總結
- 使用 Hash 實現 router
- 使用 History API 實現 router