You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/7-bank-project/1-template-route/translations
Abril Ureña 7c04992a6a
Merge pull request #974 from kant/patch-7
2 years ago
..
README.es.md Semantic issue (paragraph 270) in Spanish translation 2 years ago
README.fr.md update tracking links with / and remove MLSA link 2 years ago
README.hi.md update tracking links with / and remove MLSA link 2 years ago
README.it.md added new quiz app link 2 years ago
README.ja.md update tracking links with / and remove MLSA link 2 years ago
README.ko.md update tracking links with / and remove MLSA link 2 years ago
README.ms.md update tracking links with / and remove MLSA link 2 years ago
README.zh-tw.md update tracking links with / and remove MLSA link 2 years ago
assignment.es.md folder names 4 years ago
assignment.fr.md Translate 7-1-template-route assignment into french (#440) 3 years ago
assignment.hi.md Updated 7.1 to hindi 4 years ago
assignment.it.md Ch. 7 - italian translation 4 years ago
assignment.ja.md translate 1-template-route into japanese 4 years ago
assignment.ko.md Translate assignment.ko.md via GitLocalize 4 years ago
assignment.ms.md translated to Malay 4 years ago
assignment.nl.md Big commit 4 years ago
assignment.zh-tw.md translate lesson 7 to zh-tw 4 years ago

README.zh-tw.md

建立銀行網頁應用程式 Part 1HTML 模板與網頁路由

課前測驗

課前測驗

大綱

自從 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 模板(templates)

如果你想在同一個網頁上建立不同的畫面,其中一種方法是各自建立一個 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 屬性嗎?我們可以使用別的屬性,例如 classes 嗎?

利用 JavaScript 顯示模板

現在,如果你使用瀏覽器打開你的應用程式,你會看到畫面卡在 Loading... 的畫面。那是因為我們需要為它新增一些 JavaScript 的程式碼來顯示出這些 HTML 模板。

展現模板通常需要三個步驟:

  1. 在 DOM 內接收模板元素,舉例來說,使用 document.getElementById
  2. 複製模板元素,使用 cloneNode
  3. 將複製元素接到 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 = ''; 的目的為何?如果刪去它會發生什麼事?

建立網頁路由(Routing)

當提及網頁應用程式時,我們稱呼 路由(Routing) 來連接**網址(URLs)**到特定的畫面上,呈現相關內容。一個含有多個 HTML 檔的網頁,網址又象徵著檔案路徑,這能自動地完成網址與檔案的轉換。舉例來說,專案資料夾內有這些檔案:

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 來取得網址的部分路徑。

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);
}

這邊我們建立了模板的路由關係。你可以藉由修改網址,來測試你的網頁是否正確的轉移。

如果你輸入了不存在的網址,它會發生什麼事?我們該如何解決呢?

加入網頁訪問

下一個步驟為在不更改網址的情況下,新增網頁訪問的途徑。這會做出兩件事情:

  1. 更新現在的網址
  2. 更新要被顯示的模板到新的網址中

我們已經完成了第二點,藉由使用函式 updateRoute 來完成,所以我們需要釐清該如何更新現在的網址。

我們需要使用 JavaScript詳細來看為 history.pushState,更新網址位置並建立瀏覽紀錄,同時不更新整個 HTML 頁面。

筆記:網頁超連結元素 <a href> 可以建立不同網址的連接,但它預設上會讓瀏覽器重新載入 HTML 檔。我們需要手動新增 JavaScript 處理路由以避免此行為發生,在點擊事件中使用函式 preventDefault() 。

課題

我們來建立新的函式,讓應用程式得以做網頁的訪問:

function navigate(path) {
  window.history.pushState({}, path, path);
  updateRoute();
}

這個方法根據導入的路徑位置,更新了現在的網址位置,再更新模板上去。window.location.origin 回傳了網址的根路徑,允許我們重新構築完整的網址。

現在,藉由上述的函式,我們可以解決找不到網頁路徑的問題。我們修改函式 updateRoute,在找不到該網頁時強制轉移到一個存在的網頁。

function updateRoute() {
  const path = window.location.pathname;
  const route = routes[path];

  if (!route) {
    return navigate('/login');
  }

  ...

如果找不到網頁路由時,我們會導往 login 的頁面。

現在,我們建立新的函式,在連結被點擊時取得網址位置,並避免瀏覽器進行預設上的重新載入:

function onLinkClick(event) {
  event.preventDefault();
  navigate(event.target.href);
}

現在我們完成應用程式的網頁訪問系統,在 HTML 檔的 LoginLogout 連結加入此函式。

<a href="/dashboard" onclick="onLinkClick(event)">Login</a>
...
<a href="/login" onclick="onLinkClick(event)">Logout</a>

使用 onclick 屬性會將 click 事件連接到 JavaScript 程式碼中,這邊會再呼叫函式 navigate()

試著點擊這些連結,你應該能造訪網頁中不同的的畫面了。

history.pushState 這個方法是 HTML5 標準的一部份,支援在所有當代的瀏覽器上。如果你要為舊款的瀏覽器設計網頁應用程式的話,這邊有一個技巧來加在這個 API 上:在路徑前面加上 hash (#),你可以完成網頁路由與不須重載網頁的功能,它的目的就是在同一個網頁中做內部連結的切換。

處理瀏覽器的「上一頁」與「下一頁」

使用 history.pushState 會建立瀏覽器的瀏覽紀錄。你可以使用瀏覽器的上一頁來確認,它應該要能呈現像這樣的畫面:

瀏覽歷史的截圖

點擊上一頁數次,你會看到網址會改變且歷史紀錄也更新上去了,但同一個模板還是被顯示出來。

這是因為網頁不知道該如何依據歷史紀錄來呼叫 updateRoute()。如果你閱讀了 history.pushState 技術文件,你會發現如果狀態改變 ── 同時代表著網址改變 ── popstate 事件就會被觸發。我們會利用此特徵來修復這個問題。

課題

為了在瀏覽器歷史改變時更新該被顯示的模板,我們會以新函式來呼叫 updateRoute()。我們在 app.js 檔最下方加入:

window.onpopstate = () => updateRoute();
updateRoute();

筆記:我們在這裡使用箭頭函式,簡短地宣告 popstate 事件處理器。它與正規的函式的功能是一樣的。

這是關於箭頭函式的回想影片:

箭頭函式

點擊上方圖片以觀看關於箭頭函式的影片。

現在,試著點擊瀏覽器上的上一頁與下一頁,檢查這次模板是否正確地更新出來。


🚀 挑戰

加入新的模板與對應的關聯表,顯示出本應用程式第三頁的功能 ── 帳戶餘額。

課後測驗

課後測驗

複習與自學

網頁路由是網頁開發中很棘手的部分,特別是將網頁切換轉變為單一頁面應用程式(Single Page Application)。閱讀關於Azure Static Web App 提供服務的方式以處理網頁路由。你能解釋為什麼文件上的某些決定會如此重要呢?

作業

增進網頁路由