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/translations/th/7-bank-project/1-template-route/README.md

54 KiB

สร้างแอปธนาคาร ตอนที่ 1: เทมเพลต HTML และเส้นทางในเว็บแอป

เมื่อคอมพิวเตอร์นำทางของ Apollo 11 นำทางไปยังดวงจันทร์ในปี 1969 มันต้องสลับระหว่างโปรแกรมต่าง ๆ โดยไม่ต้องรีสตาร์ทระบบทั้งหมด แอปพลิเคชันเว็บสมัยใหม่ทำงานในลักษณะเดียวกัน เปลี่ยนสิ่งที่คุณเห็นโดยไม่ต้องโหลดทุกอย่างใหม่ตั้งแต่ต้น ซึ่งสร้างประสบการณ์ที่ราบรื่นและตอบสนองที่ผู้ใช้คาดหวังในปัจจุบัน

ต่างจากเว็บไซต์แบบดั้งเดิมที่โหลดหน้าเว็บทั้งหมดใหม่ทุกครั้งที่มีการโต้ตอบ แอปเว็บสมัยใหม่จะอัปเดตเฉพาะส่วนที่ต้องการเปลี่ยนแปลง วิธีการนี้คล้ายกับการที่ศูนย์ควบคุมภารกิจสลับระหว่างหน้าจอแสดงผลต่าง ๆ ในขณะที่ยังคงการสื่อสารอย่างต่อเนื่อง สร้างประสบการณ์ที่ราบรื่นที่เราเคยชิน

นี่คือสิ่งที่ทำให้ความแตกต่างชัดเจน:

แอปหลายหน้าแบบดั้งเดิม แอปหน้าเดียวสมัยใหม่
การนำทาง โหลดหน้าเว็บใหม่ทั้งหมดสำหรับแต่ละหน้าจอ
ประสิทธิภาพ ช้ากว่าเนื่องจากต้องดาวน์โหลด HTML ทั้งหมด
ประสบการณ์ผู้ใช้ หน้าเว็บกระพริบ
การแชร์ข้อมูล ยากระหว่างหน้า
การพัฒนา ต้องดูแลไฟล์ HTML หลายไฟล์

ทำความเข้าใจวิวัฒนาการ:

  • แอปแบบดั้งเดิม ต้องการคำขอเซิร์ฟเวอร์สำหรับทุกการกระทำการนำทาง
  • SPA สมัยใหม่ โหลดครั้งเดียวและอัปเดตเนื้อหาแบบไดนามิกโดยใช้ JavaScript
  • ความคาดหวังของผู้ใช้ ตอนนี้ชอบการโต้ตอบที่ทันทีและราบรื่น
  • ประโยชน์ด้านประสิทธิภาพ รวมถึงการลดแบนด์วิดท์และการตอบสนองที่เร็วขึ้น

ในบทเรียนนี้ เราจะสร้างแอปธนาคารที่มีหลายหน้าจอที่ไหลลื่นไปด้วยกันอย่างราบรื่น เช่นเดียวกับที่นักวิทยาศาสตร์ใช้เครื่องมือแบบโมดูลาร์ที่สามารถปรับเปลี่ยนได้สำหรับการทดลองต่าง ๆ เราจะใช้เทมเพลต HTML เป็นส่วนประกอบที่สามารถนำมาใช้ซ้ำได้ตามต้องการ

คุณจะได้ทำงานกับเทมเพลต HTML (พิมพ์เขียวที่นำมาใช้ซ้ำสำหรับหน้าจอต่าง ๆ), การกำหนดเส้นทางด้วย JavaScript (ระบบที่สลับระหว่างหน้าจอ), และ API ประวัติของเบราว์เซอร์ (ซึ่งทำให้ปุ่มย้อนกลับทำงานตามที่คาดหวัง) สิ่งเหล่านี้เป็นเทคนิคพื้นฐานเดียวกันที่ใช้ในเฟรมเวิร์กอย่าง React, Vue และ Angular

เมื่อจบบทเรียนนี้ คุณจะมีแอปธนาคารที่แสดงหลักการของแอปพลิเคชันหน้าเดียวแบบมืออาชีพ

แบบทดสอบก่อนเรียน

แบบทดสอบก่อนเรียน

สิ่งที่คุณต้องมี

เราจะต้องมีเซิร์ฟเวอร์เว็บในเครื่องเพื่อทดสอบแอปธนาคารของเรา ไม่ต้องกังวล มันง่ายกว่าที่คิด! หากคุณยังไม่มีเซิร์ฟเวอร์ในเครื่อง เพียงติดตั้ง Node.js และรันคำสั่ง npx lite-server จากโฟลเดอร์โปรเจกต์ของคุณ คำสั่งนี้จะช่วยเปิดเซิร์ฟเวอร์ในเครื่องและเปิดแอปของคุณในเบราว์เซอร์โดยอัตโนมัติ

การเตรียมตัว

บนคอมพิวเตอร์ของคุณ สร้างโฟลเดอร์ชื่อ bank พร้อมไฟล์ชื่อ index.html ภายใน เราจะเริ่มต้นจาก boilerplate 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>

สิ่งที่ boilerplate นี้ให้:

  • กำหนด โครงสร้างเอกสาร HTML5 ด้วยการประกาศ DOCTYPE ที่เหมาะสม
  • ตั้งค่า การเข้ารหัสตัวอักษรเป็น UTF-8 เพื่อรองรับข้อความระหว่างประเทศ
  • เปิดใช้งาน การออกแบบที่ตอบสนองด้วยแท็ก meta viewport เพื่อความเข้ากันได้กับมือถือ
  • ตั้งค่า ชื่อที่อธิบายซึ่งปรากฏในแท็บเบราว์เซอร์
  • สร้าง ส่วน body ที่สะอาดซึ่งเราจะสร้างแอปพลิเคชันของเรา

📁 ตัวอย่างโครงสร้างโปรเจกต์

เมื่อจบบทเรียนนี้ โปรเจกต์ของคุณจะมี:

bank/
├── index.html      <!-- Main HTML with templates -->
├── app.js          <!-- Routing and navigation logic -->
└── style.css       <!-- (Optional for future lessons) -->

หน้าที่ของไฟล์:

  • index.html: มีเทมเพลตทั้งหมดและให้โครงสร้างแอป
  • app.js: จัดการการกำหนดเส้นทาง การนำทาง และการจัดการเทมเพลต
  • Templates: กำหนด UI สำหรับหน้าจอเข้าสู่ระบบ แดชบอร์ด และหน้าจออื่น ๆ

เทมเพลต HTML

เทมเพลตแก้ปัญหาพื้นฐานในพัฒนาเว็บ เมื่อกูเทนเบิร์กคิดค้นการพิมพ์ตัวอักษรแบบเคลื่อนที่ในปี 1440 เขาตระหนักว่าแทนที่จะแกะสลักหน้าทั้งหมด เขาสามารถสร้างบล็อกตัวอักษรที่นำมาใช้ซ้ำได้และจัดเรียงตามต้องการ เทมเพลต HTML ทำงานบนหลักการเดียวกัน แทนที่จะสร้างไฟล์ HTML แยกสำหรับแต่ละหน้าจอ คุณกำหนดโครงสร้างที่นำมาใช้ซ้ำได้ซึ่งสามารถแสดงผลเมื่อจำเป็น

คิดว่าเทมเพลตเป็นพิมพ์เขียวสำหรับส่วนต่าง ๆ ของแอปของคุณ เช่นเดียวกับที่สถาปนิกสร้างพิมพ์เขียวครั้งเดียวและใช้มันหลายครั้งแทนที่จะวาดห้องที่เหมือนกันใหม่ เราสร้างเทมเพลตครั้งเดียวและนำมาใช้เมื่อจำเป็น เบราว์เซอร์จะเก็บเทมเพลตเหล่านี้ไว้จนกว่า JavaScript จะเปิดใช้งาน

หากคุณต้องการสร้างหน้าจอหลายหน้าสำหรับหน้าเว็บ วิธีหนึ่งคือการสร้างไฟล์ HTML หนึ่งไฟล์สำหรับทุกหน้าจอที่คุณต้องการแสดง อย่างไรก็ตาม วิธีนี้มาพร้อมกับความไม่สะดวก:

  • คุณต้องโหลด HTML ทั้งหมดใหม่เมื่อสลับหน้าจอ ซึ่งอาจช้า
  • การแชร์ข้อมูลระหว่างหน้าจอต่าง ๆ เป็นเรื่องยาก

อีกวิธีหนึ่งคือการมีไฟล์ HTML เพียงไฟล์เดียว และกำหนด HTML templates หลายตัวโดยใช้ <template> element เทมเพลตเป็นบล็อก HTML ที่นำมาใช้ซ้ำได้ซึ่งเบราว์เซอร์ไม่แสดง และต้องถูกสร้างขึ้นใน runtime โดยใช้ JavaScript

มาสร้างกันเถอะ

เรากำลังจะสร้างแอปธนาคารที่มีหน้าจอหลักสองหน้าจอ: หน้าล็อกอินและแดชบอร์ด ก่อนอื่น ให้เพิ่มองค์ประกอบ placeholder ลงใน body ของ HTML ของเรา นี่คือที่ที่หน้าจอต่าง ๆ ของเราจะปรากฏ:

<div id="app">Loading...</div>

ทำความเข้าใจ placeholder นี้:

  • สร้าง container ที่มี ID "app" ซึ่งหน้าจอทั้งหมดจะถูกแสดง
  • แสดงข้อความโหลดจนกว่า JavaScript จะเริ่มต้นหน้าจอแรก
  • ให้จุดติดตั้งเดียวสำหรับเนื้อหาแบบไดนามิกของเรา
  • ช่วยให้การกำหนดเป้าหมายจาก JavaScript ง่ายขึ้นโดยใช้ document.getElementById()

💡 เคล็ดลับ: เนื่องจากเนื้อหาขององค์ประกอบนี้จะถูกแทนที่ เราสามารถใส่ข้อความหรือตัวบ่งชี้การโหลดที่จะแสดงในขณะที่แอปกำลังโหลด

ต่อไป ให้เพิ่มเทมเพลต HTML สำหรับหน้าล็อกอินด้านล่าง สำหรับตอนนี้เราจะใส่แค่หัวข้อและส่วนที่มีลิงก์ที่เราจะใช้ในการนำทาง

<template id="login">
  <h1>Bank App</h1>
  <section>
    <a href="/dashboard">Login</a>
  </section>
</template>

การวิเคราะห์เทมเพลตล็อกอินนี้:

  • กำหนดเทมเพลตด้วยตัวระบุเฉพาะ "login" เพื่อการกำหนดเป้าหมายด้วย JavaScript
  • รวมหัวข้อหลักที่สร้างแบรนด์ของแอป
  • มีองค์ประกอบ <section> ที่มีความหมายเพื่อจัดกลุ่มเนื้อหาที่เกี่ยวข้อง
  • ให้ลิงก์นำทางที่จะนำผู้ใช้ไปยังแดชบอร์ด

จากนั้นเราจะเพิ่มเทมเพลต 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>

มาทำความเข้าใจแต่ละส่วนของแดชบอร์ดนี้:

  • จัดโครงสร้างหน้าเว็บด้วยองค์ประกอบ <header> ที่มีความหมายซึ่งมีการนำทาง
  • แสดงชื่อแอปอย่างสม่ำเสมอในหน้าจอเพื่อสร้างแบรนด์
  • ให้ลิงก์ออกจากระบบที่นำกลับไปยังหน้าล็อกอิน
  • แสดงยอดเงินบัญชีปัจจุบันในส่วนที่กำหนด
  • จัดระเบียบข้อมูลธุรกรรมโดยใช้ตาราง HTML ที่มีโครงสร้างอย่างเหมาะสม
  • กำหนดหัวตารางสำหรับคอลัมน์วันที่ รายการ และจำนวนเงิน
  • เว้นว่างส่วนเนื้อหาตารางสำหรับการฉีดเนื้อหาแบบไดนามิกในภายหลัง

💡 เคล็ดลับ: เมื่อสร้างเทมเพลต HTML หากคุณต้องการดูว่ามันจะมีลักษณะอย่างไร คุณสามารถแสดงความคิดเห็นบรรทัด <template> และ </template> โดยใส่ไว้ใน <!-- -->

ทำไมคุณคิดว่าเราต้องใช้ id ในเทมเพลต? เราสามารถใช้สิ่งอื่นเช่น class ได้หรือไม่?

ทำให้เทมเพลตมีชีวิตด้วย JavaScript

ตอนนี้เราต้องทำให้เทมเพลตของเราทำงานได้ เหมือนกับที่เครื่องพิมพ์ 3D ใช้พิมพ์เขียวดิจิทัลและสร้างวัตถุจริง JavaScript ใช้เทมเพลตที่ซ่อนอยู่ของเราและสร้างองค์ประกอบที่มองเห็นได้และโต้ตอบได้ที่ผู้ใช้สามารถเห็นและใช้งานได้

กระบวนการนี้มี 3 ขั้นตอนที่สม่ำเสมอซึ่งเป็นพื้นฐานของการพัฒนาเว็บสมัยใหม่ เมื่อคุณเข้าใจรูปแบบนี้ คุณจะสามารถรับรู้ได้ในเฟรมเวิร์กและไลบรารีหลายตัว

หากคุณลองไฟล์ HTML ปัจจุบันในเบราว์เซอร์ คุณจะเห็นว่ามันติดอยู่ที่การแสดง Loading... นั่นเป็นเพราะเราต้องเพิ่มโค้ด JavaScript เพื่อสร้างและแสดงเทมเพลต HTML

การสร้างเทมเพลตมักทำใน 3 ขั้นตอน:

  1. ดึงองค์ประกอบเทมเพลตใน DOM เช่นโดยใช้ document.getElementById
  2. โคลนองค์ประกอบเทมเพลต โดยใช้ cloneNode
  3. แนบมันเข้ากับ DOM ใต้องค์ประกอบที่มองเห็นได้ เช่นโดยใช้ appendChild
flowchart TD
    A[🔍 Step 1: Find Template] --> B[📋 Step 2: Clone Template]
    B --> C[🔗 Step 3: Attach to DOM]
    
    A1["document.getElementById('login')"] --> A
    B1["template.content.cloneNode(true)"] --> B  
    C1["app.appendChild(view)"] --> C
    
    C --> D[👁️ Template Visible to User]
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e8
    style D fill:#fff3e0

การวิเคราะห์ภาพของกระบวนการ:

  • ขั้นตอนที่ 1 ค้นหาเทมเพลตที่ซ่อนอยู่ในโครงสร้าง DOM
  • ขั้นตอนที่ 2 สร้างสำเนาที่สามารถแก้ไขได้อย่างปลอดภัย
  • ขั้นตอนที่ 3 แทรกสำเนาเข้าไปในพื้นที่หน้าเว็บที่มองเห็นได้
  • ผลลัพธ์ คือหน้าจอที่ใช้งานได้ซึ่งผู้ใช้สามารถโต้ตอบได้

ทำไมเราต้องโคลนเทมเพลตก่อนแนบเข้ากับ DOM? คุณคิดว่าจะเกิดอะไรขึ้นหากเราข้ามขั้นตอนนี้?

งาน

สร้างไฟล์ใหม่ชื่อ app.js ในโฟลเดอร์โปรเจกต์ของคุณและนำเข้าไฟล์นั้นในส่วน <head> ของ HTML:

<script src="app.js" defer></script>

การทำความเข้าใจการนำเข้า script นี้:

  • เชื่อมโยงไฟล์ JavaScript กับเอกสาร HTML ของเรา
  • ใช้แอตทริบิวต์ defer เพื่อให้สคริปต์ทำงานหลังจากการวิเคราะห์ HTML เสร็จสิ้น
  • เปิดใช้งานการเข้าถึงองค์ประกอบ DOM ทั้งหมดเนื่องจากโหลดเสร็จสมบูรณ์ก่อนการดำเนินการสคริปต์
  • ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดในปัจจุบันสำหรับการโหลดสคริปต์และประสิทธิภาพ

ตอนนี้ใน 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);
}

ทีละขั้นตอน นี่คือสิ่งที่เกิดขึ้น:

  • ค้นหาองค์ประกอบเทมเพลตโดยใช้ ID เฉพาะของมัน
  • สร้างสำเนาลึกของเนื้อหาเทมเพลตโดยใช้ cloneNode(true)
  • ค้นหา container ของแอปที่เนื้อหาจะถูกแสดง
  • ล้างเนื้อหาที่มีอยู่ใน container ของแอป
  • แทรกเนื้อหาเทมเพลตที่โคลนเข้าไปใน DOM ที่มองเห็นได้

ตอนนี้เรียกฟังก์ชันนี้ด้วยหนึ่งในเทมเพลตและดูผลลัพธ์

updateRoute('login');

สิ่งที่การเรียกฟังก์ชันนี้ทำสำเร็จ:

  • เปิดใช้งานเทมเพลตล็อกอินโดยส่ง ID เป็นพารามิเตอร์
  • แสดงให้เห็นวิธีการสลับระหว่างหน้าจอแอปต่าง ๆ ด้วยโปรแกรม
  • แสดงหน้าจอล็อกอินแทนข้อความ "Loading..."

วัตถุประสงค์ของโค้ดนี้ app.innerHTML = ''; คืออะไร? จะเกิดอะไรขึ้นหากไม่มีมัน?

การสร้างเส้นทาง

การกำหนดเส้นทางเป็นเรื่องของการเชื่อมโยง URL กับเนื้อหาที่ถูกต้อง ลองนึกถึงการที่ผู้ให้บริการโทรศัพท์ในยุคแรกใช้ switchboards เพื่อเชื่อมต่อสาย พวกเขาจะรับคำขอที่เข้ามาและกำหนดเส้นทางไปยังปลายทางที่ถูกต้อง การกำหนดเส้นทางเว็บทำงานในลักษณะเดียวกัน โดยรับคำขอ URL และกำหนดว่าเนื้อหาใดควรแสดง

โดยปกติ เซิร์ฟเวอร์เว็บจะจัดการเรื่องนี้โดยการให้บริการไฟล์ HTML ต่าง ๆ สำหรับ URL ต่าง ๆ เนื่องจากเรากำลังสร้างแอปหน้าเดียว เราจำเป็นต้องจัดการการกำหนดเส้นทางนี้ด้วยตัวเองโดยใช้ JavaScript วิธีนี้ทำให้เราสามารถควบคุมประสบการณ์ผู้ใช้และประสิทธิภาพได้มากขึ้น

flowchart LR
    A["🌐 URL Path<br/>/dashboard"] --> B["🗺️ Routes Object<br/>Lookup"]
    B --> C["🎯 Template ID<br/>'dashboard'"]
    C --> D["📄 Find Template<br/>getElementById"]
    D --> E["👁️ Display Screen<br/>Clone & Append"]
    
    F["📍 /login"] --> G["🎯 'login'"]
    H["📍 /unknown"] --> I["❌ Not Found"]
    I --> J["🔄 Redirect to /login"]
    
    style B fill:#e3f2fd
    style E fill:#e8f5e8
    style I fill:#ffebee
    style J fill:#fff3e0

การทำความเข้าใจการไหลของการกำหนดเส้นทาง:

  • การเปลี่ยนแปลง URL กระตุ้นการค้นหาในการกำหนดค่าการกำหนดเส้นทางของเรา
  • เส้นทางที่ถูกต้อง เชื่อมโยงกับ ID เทมเพลตเฉพาะสำหรับการแสดงผล
  • เส้นทางที่ไม่ถูกต้อง กระตุ้นพฤติกรรมสำรองเพื่อป้องกันสถานะที่เสียหาย
  • การแสดงผลเทมเพลต ปฏิบัติตามกระบวนการสามขั้นตอนที่เราเรียนรู้มาก่อนหน้านี้

เมื่อพูดถึงเว็บแอป เราเรียก การกำหนดเส้นทาง ว่าความตั้งใจที่จะเชื่อมโยง URL กับหน้าจอเฉพาะที่ควรแสดง ในเว็บไซต์ที่มีไฟล์ HTML หลายไฟล์ สิ่งนี้จะทำโดยอัตโนมัติเนื่องจากเส้นทางไฟล์สะท้อนอยู่ใน URL ตัวอย่างเช่น ด้วยไฟล์เหล่านี้ในโฟลเดอร์โปรเจกต์ของคุณ:

mywebsite/index.html
mywebsite/login.html
mywebsite/admin/index.html

หากคุณสร้างเซิร์ฟเวอร์เว็บโดยมี mywebsite เป็น root การแมป URL จะเป็น:

https://site.com            --> mywebsite/index.html
https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/     --> mywebsite/admin/index.html

อย่างไรก็ตาม สำหรับเว็บแอปของเรา เรากำลังใช้ไฟล์ HTML เดียวที่มีหน้าจอทั้งหมด ดังนั้นพฤติกรรมเริ่มต้นนี้ จะเกิดอะไรขึ้นถ้าคุณใส่เส้นทางที่ไม่รู้จักใน URL? เราจะแก้ไขปัญหานี้ได้อย่างไร?

เพิ่มระบบนำทาง

เมื่อมีการตั้งค่าการกำหนดเส้นทางแล้ว ผู้ใช้จำเป็นต้องมีวิธีการนำทางผ่านแอป เว็บไซต์แบบดั้งเดิมจะโหลดหน้าเว็บใหม่ทั้งหมดเมื่อคลิกลิงก์ แต่เราต้องการอัปเดตทั้ง URL และเนื้อหาโดยไม่ต้องรีเฟรชหน้าเว็บ วิธีนี้จะช่วยสร้างประสบการณ์ที่ราบรื่นเหมือนกับแอปพลิเคชันบนเดสก์ท็อปที่เปลี่ยนมุมมองต่างๆ

เราต้องประสานสองสิ่งนี้: การอัปเดต URL ของเบราว์เซอร์เพื่อให้ผู้ใช้สามารถบันทึกหน้าและแชร์ลิงก์ และการแสดงเนื้อหาที่เหมาะสม เมื่อดำเนินการอย่างถูกต้อง จะสร้างการนำทางที่ราบรื่นตามที่ผู้ใช้คาดหวังจากแอปพลิเคชันสมัยใหม่

🏗️ ข้อมูลเชิงลึกเกี่ยวกับสถาปัตยกรรม: ส่วนประกอบของระบบนำทาง

สิ่งที่คุณกำลังสร้าง:

  • 🔄 การจัดการ URL: อัปเดตแถบที่อยู่ของเบราว์เซอร์โดยไม่ต้องโหลดหน้าใหม่
  • 📋 ระบบเทมเพลต: สลับเนื้อหาแบบไดนามิกตามเส้นทางปัจจุบัน
  • 📚 การรวมประวัติ: รักษาฟังก์ชันการทำงานของปุ่มย้อนกลับ/ไปข้างหน้าของเบราว์เซอร์
  • 🛡️ การจัดการข้อผิดพลาด: การแก้ไขปัญหาอย่างราบรื่นสำหรับเส้นทางที่ไม่ถูกต้องหรือขาดหายไป

การทำงานร่วมกันของส่วนประกอบ:

  • ฟัง เหตุการณ์การนำทาง (การคลิก การเปลี่ยนแปลงประวัติ)
  • อัปเดต URL โดยใช้ History API
  • แสดงผล เทมเพลตที่เหมาะสมสำหรับเส้นทางใหม่
  • รักษา ประสบการณ์ผู้ใช้ที่ราบรื่นตลอดเวลา

ขั้นตอนต่อไปสำหรับแอปของเราคือการเพิ่มความสามารถในการนำทางระหว่างหน้าโดยไม่ต้องเปลี่ยน URL ด้วยตนเอง ซึ่งหมายถึงสองสิ่ง:

  1. อัปเดต URL ปัจจุบัน
  2. อัปเดตเทมเพลตที่แสดงผลตาม URL ใหม่

เราได้ดูแลส่วนที่สองด้วยฟังก์ชัน updateRoute แล้ว ดังนั้นเราต้องหาวิธีอัปเดต URL ปัจจุบัน

เราจะต้องใช้ JavaScript และโดยเฉพาะอย่างยิ่ง history.pushState ซึ่งช่วยให้อัปเดต URL และสร้างรายการใหม่ในประวัติการท่องเว็บโดยไม่ต้องโหลด HTML ใหม่

⚠️ หมายเหตุสำคัญ: แม้ว่าองค์ประกอบ HTML anchor <a href> สามารถใช้สร้างไฮเปอร์ลิงก์ไปยัง URL ต่างๆ ได้ด้วยตัวเอง แต่โดยค่าเริ่มต้นมันจะทำให้เบราว์เซอร์โหลด HTML ใหม่ จำเป็นต้องป้องกันพฤติกรรมนี้เมื่อจัดการการกำหนดเส้นทางด้วย JavaScript แบบกำหนดเอง โดยใช้ฟังก์ชัน preventDefault() บนเหตุการณ์คลิก

งาน

มาสร้างฟังก์ชันใหม่ที่เราสามารถใช้ในการนำทางในแอปของเรา:

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

การทำความเข้าใจฟังก์ชันการนำทางนี้:

  • อัปเดต URL ของเบราว์เซอร์ไปยังเส้นทางใหม่โดยใช้ history.pushState
  • เพิ่ม รายการใหม่ในสแต็กประวัติของเบราว์เซอร์เพื่อรองรับปุ่มย้อนกลับ/ไปข้างหน้า
  • เรียกใช้ ฟังก์ชัน updateRoute() เพื่อแสดงเทมเพลตที่เกี่ยวข้อง
  • รักษา ประสบการณ์แอปหน้าเดียวโดยไม่ต้องโหลดหน้าใหม่

วิธีนี้จะอัปเดต URL ปัจจุบันตามเส้นทางที่กำหนดก่อน แล้วจึงอัปเดตเทมเพลต คุณสมบัติ window.location.origin จะคืนค่ารากของ URL ซึ่งช่วยให้เราสร้าง URL ที่สมบูรณ์จากเส้นทางที่กำหนดได้

ตอนนี้เรามีฟังก์ชันนี้แล้ว เราสามารถจัดการปัญหาที่เกิดขึ้นเมื่อเส้นทางไม่ตรงกับเส้นทางที่กำหนดไว้ เราจะปรับเปลี่ยนฟังก์ชัน updateRoute โดยเพิ่มการแก้ไขปัญหาไปยังหนึ่งในเส้นทางที่มีอยู่หากเราไม่พบการจับคู่

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

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

  const template = document.getElementById(route.templateId);
  const view = template.content.cloneNode(true);
  const app = document.getElementById('app');
  app.innerHTML = '';
  app.appendChild(view);
}

จุดสำคัญที่ต้องจำ:

  • ตรวจสอบ ว่ามีเส้นทางสำหรับเส้นทางปัจจุบันหรือไม่
  • เปลี่ยนเส้นทาง ไปยังหน้าเข้าสู่ระบบเมื่อเข้าถึงเส้นทางที่ไม่ถูกต้อง
  • ให้ กลไกการแก้ไขปัญหาที่ป้องกันการนำทางที่เสียหาย
  • รับรอง ว่าผู้ใช้จะเห็นหน้าจอที่ถูกต้องเสมอ แม้ในกรณีที่ URL ไม่ถูกต้อง

หากไม่พบเส้นทาง เราจะเปลี่ยนเส้นทางไปยังหน้า login

ตอนนี้มาสร้างฟังก์ชันเพื่อรับ URL เมื่อคลิกลิงก์ และป้องกันพฤติกรรมลิงก์เริ่มต้นของเบราว์เซอร์:

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

การแยกย่อยตัวจัดการคลิกนี้:

  • ป้องกัน พฤติกรรมลิงก์เริ่มต้นของเบราว์เซอร์โดยใช้ preventDefault()
  • ดึง URL ปลายทางจากองค์ประกอบลิงก์ที่คลิก
  • เรียกใช้ ฟังก์ชันการนำทางแบบกำหนดเองแทนการโหลดหน้าใหม่
  • รักษา ประสบการณ์แอปหน้าเดียวที่ราบรื่น
<a href="/dashboard" onclick="onLinkClick(event)">Login</a>
...
<a href="/login" onclick="onLinkClick(event)">Logout</a>

สิ่งที่การผูก onclick นี้ทำได้:

  • เชื่อมโยง ลิงก์แต่ละลิงก์กับระบบนำทางแบบกำหนดเองของเรา
  • ส่งผ่าน เหตุการณ์คลิกไปยังฟังก์ชัน onLinkClick ของเราเพื่อประมวลผล
  • เปิดใช้งาน การนำทางที่ราบรื่นโดยไม่ต้องโหลดหน้าใหม่
  • รักษา โครงสร้าง URL ที่เหมาะสมที่ผู้ใช้สามารถบันทึกหรือแชร์ได้

แอตทริบิวต์ onclick จะผูกเหตุการณ์ click กับโค้ด JavaScript ที่นี่คือการเรียกฟังก์ชัน navigate()

ลองคลิกที่ลิงก์เหล่านี้ คุณควรจะสามารถนำทางระหว่างหน้าจอต่างๆ ของแอปของคุณได้แล้ว

เมธอด history.pushState เป็นส่วนหนึ่งของมาตรฐาน HTML5 และถูกนำไปใช้ใน เบราว์เซอร์สมัยใหม่ทั้งหมด หากคุณกำลังสร้างเว็บแอปสำหรับเบราว์เซอร์รุ่นเก่า มีเคล็ดลับที่คุณสามารถใช้แทน API นี้ได้: การใช้ hash (#) ก่อนเส้นทาง คุณสามารถใช้การกำหนดเส้นทางที่ทำงานร่วมกับการนำทางลิงก์ปกติและไม่โหลดหน้าใหม่ เนื่องจากมีวัตถุประสงค์เพื่อสร้างลิงก์ภายในหน้า

ทำให้ปุ่มย้อนกลับและไปข้างหน้าทำงาน

ปุ่มย้อนกลับและไปข้างหน้าเป็นสิ่งสำคัญสำหรับการท่องเว็บ เช่นเดียวกับที่ผู้ควบคุมภารกิจของ NASA สามารถตรวจสอบสถานะระบบก่อนหน้าได้ในระหว่างภารกิจอวกาศ ผู้ใช้คาดหวังว่าปุ่มเหล่านี้จะทำงาน และเมื่อไม่ทำงาน มันจะทำลายประสบการณ์การท่องเว็บที่คาดหวัง

แอปหน้าเดียวของเราต้องการการกำหนดค่าเพิ่มเติมเพื่อรองรับสิ่งนี้ เบราว์เซอร์รักษาสแต็กประวัติ (ซึ่งเราได้เพิ่มด้วย history.pushState) แต่เมื่อผู้ใช้นำทางผ่านประวัติ แอปของเราต้องตอบสนองโดยการอัปเดตเนื้อหาที่แสดงผลตามนั้น

sequenceDiagram
    participant User
    participant Browser
    participant App
    participant Template
    
    User->>Browser: Clicks "Login" link
    Browser->>App: onclick event triggered
    App->>App: preventDefault() & navigate('/dashboard')
    App->>Browser: history.pushState('/dashboard')
    Browser->>Browser: URL updates to /dashboard
    App->>App: updateRoute() called
    App->>Template: Find & clone dashboard template
    Template->>App: Return cloned content
    App->>Browser: Replace app content with template
    Browser->>User: Display dashboard screen
    
    Note over User,Template: User clicks browser back button
    
    User->>Browser: Clicks back button
    Browser->>Browser: History moves back to /login
    Browser->>App: popstate event fired
    App->>App: updateRoute() called automatically
    App->>Template: Find & clone login template
    Template->>App: Return cloned content
    App->>Browser: Replace app content with template
    Browser->>User: Display login screen

จุดปฏิสัมพันธ์สำคัญ:

  • การกระทำของผู้ใช้ กระตุ้นการนำทางผ่านการคลิกหรือปุ่มเบราว์เซอร์
  • แอปสกัดกั้น การคลิกลิงก์เพื่อป้องกันการโหลดหน้าใหม่
  • History API จัดการการเปลี่ยนแปลง URL และสแต็กประวัติของเบราว์เซอร์
  • เทมเพลต ให้โครงสร้างเนื้อหาสำหรับแต่ละหน้าจอ
  • ตัวฟังเหตุการณ์ รับรองว่าแอปตอบสนองต่อการนำทางทุกประเภท

การใช้ history.pushState สร้างรายการใหม่ในประวัติการนำทางของเบราว์เซอร์ คุณสามารถตรวจสอบได้โดยการกดปุ่ม ย้อนกลับ ของเบราว์เซอร์ มันควรจะแสดงบางอย่างเช่นนี้:

ภาพหน้าจอของประวัติการนำทาง

หากคุณลองคลิกปุ่มย้อนกลับหลายครั้ง คุณจะเห็นว่า URL ปัจจุบันเปลี่ยนไปและประวัติได้รับการอัปเดต แต่เทมเพลตเดียวกันยังคงแสดงผลอยู่

นั่นเป็นเพราะแอปพลิเคชันไม่ทราบว่าเราต้องเรียก updateRoute() ทุกครั้งที่ประวัติเปลี่ยนแปลง หากคุณดูเอกสาร history.pushState คุณจะเห็นว่าหากสถานะเปลี่ยนแปลง - หมายความว่าเราได้ย้ายไปยัง URL ที่แตกต่างกัน - เหตุการณ์ popstate จะถูกกระตุ้น เราจะใช้สิ่งนั้นเพื่อแก้ไขปัญหานี้

งาน

เพื่อให้แน่ใจว่าเทมเพลตที่แสดงผลได้รับการอัปเดตเมื่อประวัติของเบราว์เซอร์เปลี่ยนแปลง เราจะผูกฟังก์ชันใหม่ที่เรียก updateRoute() เราจะทำสิ่งนี้ที่ด้านล่างของไฟล์ app.js ของเรา:

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

การทำความเข้าใจการรวมประวัตินี้:

  • ฟัง เหตุการณ์ popstate ที่เกิดขึ้นเมื่อผู้ใช้นำทางด้วยปุ่มเบราว์เซอร์
  • ใช้ ฟังก์ชันลูกศรสำหรับไวยากรณ์ตัวจัดการเหตุการณ์ที่กระชับ
  • เรียกใช้ updateRoute() โดยอัตโนมัติทุกครั้งที่สถานะประวัติเปลี่ยนแปลง
  • เริ่มต้น แอปโดยการเรียก updateRoute() เมื่อหน้าโหลดครั้งแรก
  • รับรอง ว่าเทมเพลตที่ถูกต้องจะแสดงผลไม่ว่าผู้ใช้จะนำทางอย่างไร

💡 เคล็ดลับมือโปร: เราใช้ ฟังก์ชันลูกศร ที่นี่เพื่อประกาศตัวจัดการเหตุการณ์ popstate ของเราเพื่อความกระชับ แต่ฟังก์ชันปกติก็สามารถทำงานได้เหมือนกัน

นี่คือวิดีโอรีเฟรชเกี่ยวกับฟังก์ชันลูกศร:

ฟังก์ชันลูกศร

🎥 คลิกที่ภาพด้านบนเพื่อดูวิดีโอเกี่ยวกับฟังก์ชันลูกศร

ตอนนี้ลองใช้ปุ่มย้อนกลับและไปข้างหน้าของเบราว์เซอร์ของคุณ และตรวจสอบว่าเส้นทางที่แสดงผลได้รับการอัปเดตอย่างถูกต้องในครั้งนี้


ความท้าทาย GitHub Copilot Agent 🚀

ใช้โหมด Agent เพื่อทำความท้าทายต่อไปนี้:

คำอธิบาย: ปรับปรุงแอปธนาคารโดยการเพิ่มการจัดการข้อผิดพลาดและเทมเพลตหน้า 404 สำหรับเส้นทางที่ไม่ถูกต้อง เพื่อปรับปรุงประสบการณ์ผู้ใช้เมื่อไปยังหน้าที่ไม่มีอยู่

คำสั่ง: สร้างเทมเพลต HTML ใหม่ที่มี id "not-found" ซึ่งแสดงหน้า 404 ที่เป็นมิตรต่อผู้ใช้พร้อมการจัดสไตล์ จากนั้นปรับเปลี่ยนตรรกะการกำหนดเส้นทาง JavaScript เพื่อแสดงเทมเพลตนี้เมื่อผู้ใช้นำทางไปยัง URL ที่ไม่ถูกต้อง และเพิ่มปุ่ม "Go Home" ที่นำทางกลับไปยังหน้าเข้าสู่ระบบ

เรียนรู้เพิ่มเติมเกี่ยวกับ โหมด Agent ที่นี่

🚀 ความท้าทาย

เพิ่มเทมเพลตและเส้นทางใหม่สำหรับหน้าที่สามที่แสดงเครดิตสำหรับแอปนี้

เป้าหมายของความท้าทาย:

  • สร้าง เทมเพลต HTML ใหม่ที่มีโครงสร้างเนื้อหาที่เหมาะสม
  • เพิ่ม เส้นทางใหม่ในวัตถุการกำหนดค่าของเส้นทางของคุณ
  • รวม ลิงก์นำทางไปและกลับจากหน้าครดิต
  • ทดสอบ ว่าการนำทางทั้งหมดทำงานอย่างถูกต้องกับประวัติของเบราว์เซอร์

แบบทดสอบหลังการบรรยาย

แบบทดสอบหลังการบรรยาย

ทบทวนและศึกษาด้วยตนเอง

การกำหนดเส้นทางเป็นหนึ่งในส่วนที่ยุ่งยากอย่างน่าประหลาดใจของการพัฒนาเว็บ โดยเฉพาะอย่างยิ่งเมื่อเว็บเปลี่ยนจากพฤติกรรมการรีเฟรชหน้าไปเป็นการรีเฟรชหน้าในแอปพลิเคชันหน้าเดียว ลองอ่านเกี่ยวกับ วิธีที่บริการ Azure Static Web App จัดการการกำหนดเส้นทาง คุณสามารถอธิบายได้ไหมว่าทำไมการตัดสินใจบางอย่างที่อธิบายไว้ในเอกสารนั้นจึงจำเป็น?

แหล่งเรียนรู้เพิ่มเติม:

  • สำรวจ วิธีที่เฟรมเวิร์กยอดนิยมอย่าง React Router และ Vue Router ใช้การกำหนดเส้นทางฝั่งไคลเอนต์
  • ค้นคว้า ความแตกต่างระหว่างการกำหนดเส้นทางแบบ hash-based และ history API
  • เรียนรู้ เกี่ยวกับการเรนเดอร์ฝั่งเซิร์ฟเวอร์ (SSR) และวิธีที่มันส่งผลต่อกลยุทธ์การกำหนดเส้นทาง
  • ตรวจสอบ วิธีที่ Progressive Web Apps (PWAs) จัดการการกำหนดเส้นทางและการนำทาง

งานที่ได้รับมอบหมาย

ปรับปรุงการกำหนดเส้นทาง


ข้อจำกัดความรับผิดชอบ:
เอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI Co-op Translator แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลสำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราจะไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้