13 KiB
建立銀行應用程式 第1部分:網頁應用中的HTML模板與路由
課前測驗
簡介
自從JavaScript在瀏覽器中出現以來,網站變得比以往更加互動且複雜。網頁技術現在常被用來創建直接在瀏覽器中運行的完整應用程式,我們稱之為網頁應用程式。由於網頁應用程式高度互動化,使用者不希望每次執行操作時都需要重新載入整個頁面。因此,JavaScript被用來直接使用DOM更新HTML,以提供更流暢的使用者體驗。
在這節課中,我們將奠定基礎,使用HTML模板來創建銀行網頁應用,實現多個畫面顯示與更新,而無需重新載入整個HTML頁面。
先決條件
你需要一個本地網頁伺服器來測試我們在這節課中構建的網頁應用。如果你還沒有,可以安裝Node.js,並在你的專案資料夾中使用指令 npx lite-server
。這將創建一個本地網頁伺服器並在瀏覽器中打開你的應用。
準備工作
在你的電腦上,建立一個名為bank
的資料夾,並在其中建立一個名為index.html
的檔案。我們將從這個HTML樣板代碼開始:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bank App</title>
</head>
<body>
<!-- This is where you'll work -->
</body>
</html>
HTML模板
如果你想為一個網頁創建多個畫面,一種解決方案是為每個要顯示的畫面創建一個HTML檔案。然而,這種解決方案有一些不便之處:
- 切換畫面時需要重新載入整個HTML,這可能會很慢。
- 在不同畫面之間共享數據會變得困難。
另一種方法是只使用一個HTML檔案,並使用<template>
元素定義多個HTML模板。模板是一個可重複使用的HTML區塊,瀏覽器不會直接顯示它,需要在運行時使用JavaScript實例化。
任務
我們將創建一個具有兩個畫面的銀行應用:登入頁面和儀表板。首先,在HTML的body
中添加一個佔位元素,我們將用它來實例化應用的不同畫面:
<div id="app">Loading...</div>
我們給它一個id
,以便稍後用JavaScript更容易找到它。
提示:由於這個元素的內容會被替換,我們可以在其中放置一個加載消息或指示器,當應用加載時會顯示它。
接下來,在下面添加登入頁面的HTML模板。目前,我們只在其中放置一個標題和一個包含導航連結的區塊。
<template id="login">
<h1>Bank App</h1>
<section>
<a href="/dashboard">Login</a>
</section>
</template>
然後,我們再添加一個儀表板頁面的HTML模板。這個頁面將包含不同的區塊:
- 一個包含標題和登出連結的標頭
- 銀行帳戶的當前餘額
- 一個以表格形式顯示的交易記錄
<template id="dashboard">
<header>
<h1>Bank App</h1>
<a href="/login">Logout</a>
</header>
<section>
Balance: 100$
</section>
<section>
<h2>Transactions</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Object</th>
<th>Amount</th>
</tr>
</thead>
<tbody></tbody>
</table>
</section>
</template>
提示:在創建HTML模板時,如果你想查看它的外觀,可以將
<template>
和</template>
行用<!-- -->
註解掉。
✅ 你認為我們為什麼要在模板上使用id
屬性?我們是否可以使用其他東西,比如類別?
使用JavaScript顯示模板
如果你在瀏覽器中嘗試當前的HTML檔案,你會看到它卡在顯示Loading...
。這是因為我們需要添加一些JavaScript代碼來實例化並顯示HTML模板。
實例化模板通常分為三個步驟:
- 在DOM中檢索模板元素,例如使用
document.getElementById
。 - 使用
cloneNode
克隆模板元素。 - 將其附加到可見元素下的DOM中,例如使用
appendChild
。
✅ 為什麼我們需要在附加到DOM之前克隆模板?如果跳過這一步,你認為會發生什麼?
任務
在你的專案資料夾中創建一個名為app.js
的新檔案,並在HTML的<head>
部分中導入該檔案:
<script src="app.js" defer></script>
現在在app.js
中,我們將創建一個新函數updateRoute
:
function updateRoute(templateId) {
const template = document.getElementById(templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
這裡我們執行的正是上述的三個步驟。我們實例化具有templateId
的模板,並將其克隆的內容放入應用的佔位元素中。注意,我們需要使用cloneNode(true)
來複製模板的整個子樹。
現在用其中一個模板調用這個函數,並查看結果。
updateRoute('login');
✅ 這段代碼app.innerHTML = '';
的目的是什麼?如果沒有它會發生什麼?
創建路由
在談到網頁應用時,我們稱路由為將URL映射到應顯示的特定畫面的意圖。在具有多個HTML檔案的網站中,這是自動完成的,因為檔案路徑會反映在URL中。例如,在你的專案資料夾中有以下檔案:
mywebsite/index.html
mywebsite/login.html
mywebsite/admin/index.html
如果你以mywebsite
作為根目錄創建一個網頁伺服器,URL映射將是:
https://site.com --> mywebsite/index.html
https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
然而,對於我們的網頁應用,我們使用的是包含所有畫面的單一HTML檔案,因此這種默認行為對我們沒有幫助。我們必須手動創建這個映射,並使用JavaScript更新顯示的模板。
任務
我們將使用一個簡單的物件來實現一個映射,將URL路徑與我們的模板對應起來。在app.js
檔案的頂部添加這個物件。
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard' },
};
現在讓我們稍微修改一下updateRoute
函數。我們不再直接將templateId
作為參數傳遞,而是希望通過首先查看當前URL,然後使用我們的映射來獲取對應的模板ID值。我們可以使用window.location.pathname
來僅獲取URL中的路徑部分。
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
這裡我們將聲明的路由映射到對應的模板。你可以嘗試手動更改瀏覽器中的URL,檢查它是否正確運作。
✅ 如果你在URL中輸入一個未知的路徑會發生什麼?我們該如何解決這個問題?
添加導航功能
我們應用的下一步是添加在頁面之間導航的功能,而無需手動更改URL。這涉及兩件事:
- 更新當前URL
- 根據新URL更新顯示的模板
第二部分我們已經通過updateRoute
函數處理了,所以我們需要弄清楚如何更新當前URL。
我們需要使用JavaScript,具體來說是history.pushState
,它允許在不重新載入HTML的情況下更新URL並在瀏覽歷史中創建新條目。
注意:雖然HTML的錨點元素
<a href>
本身可以用來創建指向不同URL的超連結,但它默認會使瀏覽器重新載入HTML。在處理自定義JavaScript路由時,需要使用preventDefault()
函數來防止這種行為。
任務
讓我們創建一個新函數,用於在應用中導航:
function navigate(path) {
window.history.pushState({}, path, path);
updateRoute();
}
這個方法首先根據給定的路徑更新當前URL,然後更新模板。屬性window.location.origin
返回URL的根目錄,允許我們從給定路徑重建完整URL。
現在我們有了這個函數,我們可以解決當路徑與任何定義的路由不匹配時的問題。我們將通過在updateRoute
函數中添加一個回退到現有路由的功能來解決這個問題。
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
if (!route) {
return navigate('/login');
}
...
如果找不到路由,我們現在會重定向到login
頁面。
接下來,我們創建一個函數來獲取點擊連結時的URL,並防止瀏覽器的默認連結行為:
function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}
最後,通過在HTML中的登入和登出連結上添加綁定來完成導航系統。
<a href="/dashboard" onclick="onLinkClick(event)">Login</a>
...
<a href="/login" onclick="onLinkClick(event)">Logout</a>
上述代碼中的event
物件捕獲click
事件並將其傳遞給我們的onLinkClick
函數。
使用onclick
屬性將click
事件綁定到JavaScript代碼,這裡是對navigate()
函數的調用。
嘗試點擊這些連結,你現在應該能夠在應用的不同畫面之間導航。
✅ history.pushState
方法是HTML5標準的一部分,並在所有現代瀏覽器中實現。如果你正在為舊版瀏覽器構建網頁應用,有一個替代這個API的技巧:使用路徑前的哈希符號(#
),你可以實現基於常規錨點導航的路由,且不會重新載入頁面,因為它的目的是在頁面內創建內部連結。
處理瀏覽器的返回與前進按鈕
使用history.pushState
會在瀏覽器的導航歷史中創建新條目。你可以通過按住瀏覽器的返回按鈕來檢查,它應該顯示如下內容:
如果你嘗試多次點擊返回按鈕,你會看到當前URL發生了變化,歷史也被更新,但顯示的模板保持不變。
這是因為應用不知道每次歷史變化時需要調用updateRoute()
。如果你查看history.pushState
文檔,你會看到當狀態改變時(即我們移動到不同的URL),會觸發popstate
事件。我們將利用這一點來解決這個問題。
任務
為了確保當瀏覽器歷史變化時顯示的模板被更新,我們將附加一個新函數來調用updateRoute()
。我們會在app.js
檔案的底部完成這件事:
window.onpopstate = () => updateRoute();
updateRoute();
注意:我們在這裡使用了一個箭頭函數來聲明
popstate
事件處理器以簡潔,但普通函數也可以正常工作。
這裡有一個關於箭頭函數的回顧影片:
🎥 點擊上方圖片觀看關於箭頭函數的影片。
現在嘗試使用瀏覽器的返回與前進按鈕,檢查這次顯示的路由是否正確更新。
🚀 挑戰
為這個應用新增一個模板和路由,顯示應用的製作團隊名單。
課後測驗
回顧與自學
路由是網頁開發中出乎意料的棘手部分之一,特別是隨著網頁從頁面刷新行為轉向單頁應用的頁面刷新。閱讀一些關於Azure靜態網頁應用服務如何處理路由的內容。你能解釋為什麼文檔中描述的一些決策是必要的嗎?
作業
免責聲明:
此文件已使用 AI 翻譯服務 Co-op Translator 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。