Progressive Web App แรกของคุณ

อัปเดตล่าสุด: 2019-04-30

องค์ประกอบของ Progressive Web App คืออะไร

Progressive Web App มอบประสบการณ์ที่ดูคล้ายกับแอปได้บนเดสก์ท็อปและอุปกรณ์เคลื่อนที่ ซึ่งสร้างขึ้นและนําส่งโดยตรงผ่านเว็บ และเป็นเว็บแอปที่ทํางานรวดเร็วและเชื่อถือได้ สิ่งสําคัญที่สุดคือเว็บแอปทํางานได้ในเบราว์เซอร์ทุกชนิด หากปัจจุบันคุณสร้างเว็บแอปนี้อยู่ เท่ากับคุณกําลังสร้าง Progressive Web App อยู่แล้ว

รวดเร็วและน่าเชื่อถือ

ทุกประสบการณ์บนเว็บต้องรวดเร็ว และโดยเฉพาะในส่วนของ Progressive Web App คําว่า "เร็ว" หมายถึงระยะเวลาที่ใช้เพื่อแสดงเนื้อหาที่มีความหมายบนหน้าจอและมอบประสบการณ์แบบอินเทอร์แอกทีฟ

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

ติดตั้งได้

Progressive Web App ทํางานในแท็บเบราว์เซอร์ได้ แต่ยังติดตั้งได้ด้วย การบุ๊กมาร์กเว็บไซต์เป็นเพียงการเพิ่มทางลัดเท่านั้น แต่ Progressive Web App (PWA) ที่ติดตั้งไว้จะมีลักษณะและมีลักษณะเหมือนกับแอปอื่นๆ ที่ติดตั้งไว้ทั้งหมด โดยจะเปิดจากที่เดียวกันกับการเปิดตัวแอปอื่นๆ คุณสามารถควบคุมประสบการณ์ในการเปิดตัวได้ รวมถึงหน้าจอแนะนํา ไอคอน และอื่นๆ ที่กําหนดเอง ซึ่งจะทํางานเป็นแอปในหน้าต่างแอปโดยไม่มีแถบที่อยู่หรือ UI อื่นๆ ของเบราว์เซอร์ เช่นเดียวกับแอปที่ติดตั้งไว้อื่นๆ แอปนี้จะเป็นแอประดับสูงสุดในตัวสลับงาน

อย่าลืมว่า PWA ที่ติดตั้งได้นั้นรวดเร็วและเชื่อถือได้ ผู้ใช้ที่ติดตั้ง PWA คาดหวังว่าแอปจะทํางานได้ไม่ว่าจะใช้การเชื่อมต่อเครือข่ายประเภทใด ซึ่งเป็นความคาดหวังพื้นฐานที่ทุกแอปที่ติดตั้งไว้ต้องปฏิบัติตาม

อุปกรณ์เคลื่อนที่และเดสก์ท็อป

การใช้เทคนิคการออกแบบที่ปรับเปลี่ยนตามอุปกรณ์ PWA ทํางานทั้งในอุปกรณ์เคลื่อนที่และเดสก์ท็อป โดยใช้ฐานโค้ดเดียวระหว่างแพลตฟอร์มต่างๆ หากคุณกําลังพิจารณาเขียนแอปที่มาพร้อมเครื่อง โปรดดูประโยชน์ที่ PWA มีให้

สิ่งที่คุณจะสร้าง

ใน Codelab นี้ คุณจะสร้างเว็บแอปสภาพอากาศโดยใช้เทคนิค PWA แอปจะทําสิ่งต่อไปนี้

  • ใช้การออกแบบที่ปรับเปลี่ยนตามอุปกรณ์เพื่อให้ทํางานบนเดสก์ท็อปหรืออุปกรณ์เคลื่อนที่ได้
  • ทํางานเร็วขึ้นโดยใช้ Service Worker เพื่อแคชทรัพยากรล่วงหน้าของแอป (HTML, CSS, JavaScript, รูปภาพ) ที่จําเป็นต่อการทํางานและแคชข้อมูลสภาพอากาศขณะรันไทม์เพื่อปรับปรุงประสิทธิภาพ
  • ติดตั้งได้ โดยใช้ไฟล์ Manifest ของเว็บแอปและเหตุการณ์ beforeinstallprompt เพื่อแจ้งผู้ใช้ว่าติดตั้งได้

สิ่งที่จะได้เรียนรู้

  • วิธีสร้างและเพิ่มไฟล์ Manifest ของเว็บแอป
  • วิธีมอบประสบการณ์การใช้งานออฟไลน์ที่เรียบง่าย
  • วิธีมอบประสบการณ์การใช้งานแบบออฟไลน์อย่างเต็มรูปแบบ
  • วิธีทําให้แอปสามารถติดตั้งได้

Codelab นี้มุ่งเน้นที่ Progressive Web App แนวคิดที่ไม่มีความเกี่ยวข้องและการบล็อกโค้ดจะไม่เพียงพอ และคุณก็มีหน้าที่คัดลอกและวางเท่านั้น

สิ่งที่ต้องมี

  • Chrome เวอร์ชันล่าสุด (74 ขึ้นไป) PWA ใช้ได้ในเบราว์เซอร์ทั้งหมด แต่เราจะใช้ฟีเจอร์ 2-3 อย่างของ Chrome DevTools เพื่อทําความเข้าใจสิ่งที่เกิดขึ้นในระดับเบราว์เซอร์ และจะใช้เพื่อทดสอบประสบการณ์การติดตั้ง
  • ความรู้เกี่ยวกับ HTML, CSS, JavaScript และ Chrome DevTools

ดาวน์โหลดคีย์สําหรับ Dark Sky API

ข้อมูลสภาพอากาศมาจาก Dark Sky API คุณจะต้องขอคีย์ API เพื่อใช้งาน ใช้งานง่ายและไม่มีค่าใช้จ่ายสําหรับโครงการที่ไม่ใช่เชิงพาณิชย์

ลงทะเบียนคีย์ API

ยืนยันว่าคีย์ API ทํางานได้อย่างถูกต้อง

หากต้องการทดสอบว่าคีย์ API ทํางานอย่างถูกต้อง ให้ส่งคําขอ HTTP ไปยัง DarkSky API อัปเดต URL ด้านล่างเพื่อแทนที่ DARKSKY_API_KEY ด้วยคีย์ API ของคุณ หากทุกอย่างเรียบร้อยดี คุณจะเห็นการพยากรณ์อากาศล่าสุดสําหรับนิวยอร์กซิตี้

https://api.darksky.net/forecast/DARKSKY_API_KEY/40.7720232,-73.9732319

รับรหัส

เราได้เก็บข้อมูลทุกอย่างที่จําเป็นสําหรับโปรเจ็กต์นี้ลงในที่เก็บ Git ในการเริ่มต้นใช้งาน คุณจะต้องรับโค้ดและเปิดในสภาพแวดล้อมนักพัฒนาซอฟต์แวร์ที่คุณชื่นชอบ เราขอแนะนําให้ใช้ Glitch สําหรับ Codelab นี้

แนะนําอย่างยิ่ง: ใช้ Glitch เพื่อนําเข้าที่เก็บ

การใช้ Glitch เป็นวิธีที่แนะนําสําหรับการเรียกใช้ Codelab นี้

  1. เปิดแท็บใหม่ของเบราว์เซอร์แล้วไปที่ https://glitch.com
  2. หากยังไม่มีบัญชี คุณจะต้องลงชื่อสมัครใช้
  3. คลิกการฉายภาพใหม่ จากนั้นคลิกโคลนจาก Git Repo
  4. จําลอง https://github.com/googlecodelabs/your-first-pwapp.git แล้วคลิกตกลง
  5. เมื่อโหลดที่เก็บแล้ว ให้แก้ไขไฟล์ .env และอัปเดตด้วยคีย์ API ของ DarkSky
  6. คลิกปุ่มแสดง แล้วเลือกในหน้าต่างใหม่เพื่อดูการทํางานของ PWA

ทางเลือก: ดาวน์โหลดโค้ด &ทํางานภายในเครื่อง

หากต้องการดาวน์โหลดโค้ดและทํางานในเครื่อง คุณจะต้องมี Node.js เวอร์ชันล่าสุดและตั้งค่าตัวแก้ไขโค้ดและพร้อมใช้งาน

ดาวน์โหลดซอร์สโค้ด

  1. คลายการคลายไฟล์ ZIP ที่ดาวน์โหลด
  2. เรียกใช้ npm install เพื่อติดตั้งทรัพยากร Dependency ที่ต้องใช้ในการเรียกใช้เซิร์ฟเวอร์
  3. แก้ไข server.js และตั้งค่าคีย์ API ของ DarkSky
  4. เรียกใช้ node server.js เพื่อเริ่มเซิร์ฟเวอร์ในพอร์ต 8000
  5. เปิดแท็บเบราว์เซอร์ไปยัง http://localhost:8000

จุดเริ่มต้นของเราคืออะไร

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

สิ่งที่ควรลอง...

  1. เพิ่มเมืองใหม่ด้วยปุ่มสีฟ้า + ที่มุมขวาล่าง
  2. รีเฟรชข้อมูลด้วยปุ่มรีเฟรชที่มุมบนขวา
  3. ลบเมืองโดยใช้ x ที่ด้านขวาบนของการ์ดเมืองแต่ละรายการ
  4. ใช้แถบเครื่องมือของอุปกรณ์สลับกับเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome เพื่อดูวิธีการทํางานของเครื่องมือบนเดสก์ท็อปและอุปกรณ์เคลื่อนที่
  5. ใช้แผงเครือข่ายในเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome เพื่อดูสิ่งที่จะเกิดขึ้นเมื่อคุณออฟไลน์
  6. ใช้แผงเครือข่ายใน Chrome DevTools เพื่อดูสิ่งที่จะเกิดขึ้นเมื่อมีการควบคุมเครือข่ายเป็น 3G แบบช้า
  7. เพิ่มความล่าช้าไปยังเซิร์ฟเวอร์การคาดการณ์โดยการเปลี่ยนค่าสําหรับ FORECAST_DELAY ใน server.js

การตรวจสอบด้วย Lighthouse

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

เราจะใช้ Lighthouse เพื่อตรวจสอบแอปสภาพอากาศของเรา และยืนยันการเปลี่ยนแปลงที่เราทํา

มาเรียกใช้ Lighthouse กัน

  1. เปิดโปรเจ็กต์ในแท็บใหม่
  2. เปิด Chrome DevTools และเปลี่ยนไปใช้แผงการตรวจสอบ เปิดใช้ประเภทการตรวจสอบทั้งหมดไว้
  3. คลิกเรียกใช้การตรวจสอบ หลังจากนั้น Lighthouse จะแสดงรายงานในหน้าเว็บ

Progressive Web App Audit

เราจะเน้นที่ผลการตรวจสอบ Progressive Web App

และมีสีแดงมากมายที่ต้องมุ่งเน้น

  • ❗FAILED: หน้าปัจจุบันไม่ตอบสนองด้วย 200 เมื่อออฟไลน์
  • ❗FAILED: start_url ไม่ตอบสนองด้วย 200 เมื่อออฟไลน์
  • ❗FAILED: ไม่ได้ลงทะเบียน Service Worker ที่ควบคุมหน้าและ start_url.
  • ❗FAILED: ไฟล์ Manifest ของเว็บแอปไม่เป็นไปตามข้อกําหนดความสามารถในการติดตั้ง
  • ❗FAILED: ไม่ได้กําหนดค่าสําหรับหน้าจอแนะนําที่กําหนดเอง
  • ❗FAILED: ไม่ได้ตั้งค่าสีธีมของแถบที่อยู่

มาเริ่มแก้ไขปัญหาเหล่านี้กัน

เมื่อจบส่วนนี้ แอปสภาพอากาศของเราจะผ่านการตรวจสอบต่อไปนี้

  • ไฟล์ Manifest ของเว็บแอปไม่เป็นไปตามข้อกําหนดความสามารถในการติดตั้ง
  • ไม่ได้กําหนดค่าสําหรับหน้าจอแนะนําที่กําหนดเอง
  • ไม่ได้ตั้งค่าสีธีมของแถบที่อยู่

สร้างไฟล์ Manifest ของเว็บแอป

ไฟล์ Manifest ของเว็บแอปเป็นไฟล์ JSON ที่เรียบง่ายซึ่งช่วยให้คุณซึ่งเป็นนักพัฒนาแอปควบคุมวิธีที่แอปปรากฏต่อผู้ใช้ได้

เมื่อใช้ไฟล์ Manifest ของเว็บแอป เว็บแอปจะทําสิ่งต่อไปนี้ได้

  • แจ้งเบราว์เซอร์ที่คุณต้องการให้เปิดแอปในหน้าต่างแบบสแตนด์อโลน (display)
  • กําหนดหน้าที่จะเปิดขึ้นมาเมื่อมีการเปิดแอปครั้งแรก (start_url)
  • กําหนดลักษณะของแอปที่จะใช้บนแถบหรือตัวเปิดแอป (short_name, icons)
  • สร้างหน้าจอแนะนํา (name, icons, colors)
  • บอกให้เบราว์เซอร์เปิดหน้าต่างในแนวนอนหรือโหมดแนวตั้ง (orientation)
  • และอีกมากมาย

สร้างไฟล์ชื่อ public/manifest.json ในโปรเจ็กต์ คัดลอกและวางเนื้อหาต่อไปนี้

public/manifest.json

{
  "name": "Weather",
  "short_name": "Weather",
  "icons": [{
    "src": "/images/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-256x256.png",
      "sizes": "256x256",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }],
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#3E4EB8",
  "theme_color": "#2F3BA2"
}

ไฟล์ Manifest รองรับอาร์เรย์ของไอคอนสําหรับหน้าจอขนาดต่างๆ สําหรับ Codelab นี้ เราได้รวมสิทธิ์อื่นๆ สําหรับการผสานรวม iOS ไว้ด้วย

ถัดไป เราจะต้องแจ้งเบราว์เซอร์เกี่ยวกับไฟล์ Manifest ด้วยการเพิ่ม <link rel="manifest"... ไปยังหน้าแต่ละหน้าในแอปของเรา เพิ่มบรรทัดต่อไปนี้ลงในองค์ประกอบ <head> ในไฟล์ index.html

public/index.html

<!-- CODELAB: Add link rel manifest -->
<link rel="manifest" href="/manifest.json">

เปลี่ยนเส้นทางเครื่องมือสําหรับนักพัฒนาเว็บ

เครื่องมือสําหรับนักพัฒนาเว็บช่วยให้คุณตรวจสอบไฟล์ manifest.json ได้อย่างรวดเร็วและง่ายดาย เปิดแผงไฟล์ Manifest ในแผงแอปพลิเคชัน หากคุณเพิ่มข้อมูลไฟล์ Manifest อย่างถูกต้อง คุณจะดูได้ว่าข้อมูลได้รับการแยกวิเคราะห์และแสดงในรูปแบบที่เป็นมิตรกับมนุษย์ในแผงนี้

เพิ่มเมตาแท็ก iOS และไอคอน

Safari ใน iOS ไม่รองรับไฟล์ Manifest ของเว็บแอป (Yet) ดังนั้นคุณจะต้องเพิ่มแท็ก meta แบบดั้งเดิมลงใน <head> ของไฟล์ index.html ดังนี้

public/index.html

<!-- CODELAB: Add iOS meta tags and icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Weather PWA">
<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png">

โบนัส: การแก้ไขง่ายๆ ใน Lighthouse

การตรวจสอบของ Lighthouse ได้พูดถึงผลลัพธ์อื่นๆ ที่แก้ไขได้ง่ายแล้ว ดังนั้นเราจะช่วยจัดการปัญหาเหล่านั้นในขณะที่กําลังอยู่ที่นี่

ตั้งค่าคําอธิบายเมตา

ใต้การตรวจสอบ SEO นั้น Lighthouse ระบุว่า "เอกสารไม่มีคําอธิบายเมตา" คําอธิบายอาจแสดงในผลการค้นหาของ Google Search ได้ คําอธิบายที่มีคุณภาพสูงและไม่เหมือนใครสามารถช่วยให้ผลการค้นหาของคุณมีความเกี่ยวข้องกับผู้ใช้ Search มากขึ้น และสามารถเพิ่มปริมาณการค้นหาได้

หากต้องการเพิ่มคําอธิบาย ให้เพิ่มแท็ก meta ต่อไปนี้ลงใน <head> ของเอกสาร

public/index.html

<!-- CODELAB: Add description here -->
<meta name="description" content="A sample weather app">

กําหนดสีธีมของแถบที่อยู่

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

หากต้องการตั้งค่าสีธีมในอุปกรณ์เคลื่อนที่ ให้เพิ่มแท็ก meta ต่อไปนี้ลงใน <head> ของเอกสาร

public/index.html

<!-- CODELAB: Add meta theme-color -->
<meta name="theme-color" content="#2F3BA2" />

ยืนยันการเปลี่ยนแปลงด้วย Lighthouse

เรียกใช้ Lighthouse อีกครั้ง (โดยคลิก + เครื่องหมายที่มุมซ้ายบนของแผงการตรวจสอบ) แล้วยืนยันการเปลี่ยนแปลง

การตรวจสอบ SEO

  • 📲 ผ่าน: เอกสารมีคําอธิบายเมตา

Progressive Web App Audit

  • ❗FAILED: หน้าปัจจุบันไม่ตอบสนองด้วย 200 เมื่อออฟไลน์
  • ❗FAILED: start_url ไม่ตอบสนองด้วย 200 เมื่อออฟไลน์
  • ❗FAILED: ไม่ได้ลงทะเบียน Service Worker ที่ควบคุมหน้าและ start_url.
  • 📲 ผ่าน: ไฟล์ Manifest ของเว็บแอปเป็นไปตามข้อกําหนดด้านความสามารถในการติดตั้ง
  • 📲 ผ่าน: กําหนดค่าสําหรับหน้าจอแนะนําที่กําหนดเอง
  • ⚠ ผ่าน: ตั้งค่าสีธีมของแถบที่อยู่

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

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

  • หน้าปัจจุบันไม่ตอบสนองด้วย 200 เมื่อออฟไลน์
  • start_url ไม่ตอบสนองด้วยรหัส 200 เมื่อออฟไลน์
  • ไม่ได้ลงทะเบียน Service Worker ที่ควบคุมหน้าและ start_url.

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

Service Worker ถึงแผนกช่วยเหลือ

หากคุณไม่คุ้นเคยกับ Service Worker คุณก็สามารถทําความเข้าใจพื้นฐานเกี่ยวกับสิ่งที่คุณทําได้ วงจรการทํางาน และอื่นๆ ได้จากข้อมูลเบื้องต้นเกี่ยวกับ Service Worker

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

ลงทะเบียน Service Worker

ขั้นตอนแรกคือการลงทะเบียน Service Worker เพิ่มโค้ดต่อไปนี้ในไฟล์ index.html

public/index.html

// CODELAB: Register service worker.
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
        .then((reg) => {
          console.log('Service worker registered.', reg);
        });
  });
}

โค้ดนี้จะตรวจสอบว่า Service Worker API พร้อมใช้งานหรือไม่ (หากมี) และมี Service Worker ที่ /service-worker.js ลงทะเบียนเมื่อหน้าเว็บโหลดแล้ว

โปรดทราบว่า Service Worker จะแสดงจากไดเรกทอรีราก ไม่ใช่จากไดเรกทอรี /scripts/ นี่เป็นวิธีที่ง่ายที่สุดในการตั้งค่า scope ของ Service Worker scope ของ Service Worker จะเป็นตัวกําหนดว่าโปรแกรมทํางานของบริการใดควบคุมไฟล์อยู่ scope เริ่มต้นคือตําแหน่งของไฟล์ Service Worker และขยายไปยังไดเรกทอรีทั้งหมดด้านล่าง ดังนั้นหาก service-worker.js อยู่ในไดเรกทอรีราก Service Worker จะควบคุมคําขอจากหน้าเว็บทั้งหมดที่โดเมนนี้

แคชหน้าแบบออฟไลน์ล่วงหน้า

ก่อนอื่น เราต้องแจ้งให้พนักงานของบริการทราบว่าจะแคชอะไรบ้าง เราได้สร้างหน้าออฟไลน์ (public/offline.html) แบบง่ายซึ่งเราจะแสดงได้ทุกเมื่อหากไม่มีการเชื่อมต่อเครือข่าย

ใน service-worker.js ให้เพิ่ม '/offline.html', ลงในอาร์เรย์ FILES_TO_CACHE ผลลัพธ์สุดท้ายควรมีลักษณะดังนี้

public/service-worker.js

// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
  '/offline.html',
];

ถัดไป เราจะต้องเพิ่มโค้ดต่อไปนี้ลงในเหตุการณ์ install เพื่อแจ้งให้ Service Worker แคชหน้าแบบออฟไลน์ล่วงหน้า

public/service-worker.js

// CODELAB: Precache static resources here.
evt.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      console.log('[ServiceWorker] Pre-caching offline page');
      return cache.addAll(FILES_TO_CACHE);
    })
);

ตอนนี้เหตุการณ์ install เปิดแคชด้วย caches.open() และระบุชื่อแคชแล้ว การระบุชื่อแคชจะช่วยให้เรากําหนดเวอร์ชันไฟล์หรือแยกข้อมูลจากทรัพยากรที่แคชไว้เพื่อให้เราอัปเดตได้อย่างง่ายดาย แต่ไม่ส่งผลกระทบต่ออีกรายการ

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

เปลี่ยนเส้นทางเครื่องมือสําหรับนักพัฒนาเว็บ

มาดูวิธีใช้ DevTools เพื่อทําความเข้าใจและแก้ไขข้อบกพร่องของ Service Worker กัน ก่อนโหลดหน้าเว็บซ้ํา ให้เปิด DevTools แล้วไปที่แผง Service Worker ในแผงแอปพลิเคชัน ซึ่งควรมีลักษณะดังนี้

เมื่อคุณเห็นหน้าว่างเปล่าเช่นนี้ หมายความว่าหน้าที่เปิดอยู่ในปัจจุบันไม่มี Service Worker ใดๆ ที่ลงทะเบียน

โหลดหน้านี้ซ้ํา แผง Service Worker ควรมีลักษณะเช่นนี้

เมื่อเห็นข้อมูลเช่นนี้ หมายความว่าหน้าดังกล่าวมี Service Worker ทํางานอยู่

ถัดจากป้ายกํากับสถานะจะมีตัวเลข (34251 ในกรณีนี้) จับตาดูหมายเลขดังกล่าวขณะทํางานกับ Service Worker เป็นวิธีที่บอกได้ง่ายๆ ว่ามีการอัปเดต Service Worker หรือไม่

ล้างหน้าออฟไลน์เก่าๆ

เราจะใช้เหตุการณ์ activate เพื่อล้างข้อมูลเก่าในแคชของเรา โค้ดนี้ช่วยให้ Service Worker อัปเดตแคชทุกครั้งที่มีการเปลี่ยนไฟล์ Shell ของแอป เพื่อให้ทํางานได้ คุณจะต้องเพิ่มตัวแปร CACHE_NAME ที่ด้านบนของไฟล์ Service Worker

เพิ่มโค้ดต่อไปนี้ในกิจกรรม activate

public/service-worker.js

// CODELAB: Remove previous cached data from disk.
evt.waitUntil(
    caches.keys().then((keyList) => {
      return Promise.all(keyList.map((key) => {
        if (key !== CACHE_NAME) {
          console.log('[ServiceWorker] Removing old cache', key);
          return caches.delete(key);
        }
      }));
    })
);

เปลี่ยนเส้นทางเครื่องมือสําหรับนักพัฒนาเว็บ

เมื่อแผง Service Worker เปิดอยู่ ให้รีเฟรชหน้า คุณจะเห็น Service Worker ใหม่และหมายเลขสถานะเพิ่มขึ้น

Service Worker ที่อัปเดตจะควบคุมทันทีเนื่องจากเหตุการณ์ install ของเราเสร็จสิ้นด้วย self.skipWaiting() และกิจกรรม activate จะสิ้นสุดด้วย self.clients.claim() หากไม่มีลิงก์ดังกล่าว โปรแกรมทํางานของบริการเก่าจะยังคงควบคุมหน้าเว็บต่อไปตราบใดที่มีแท็บเปิดอยู่

จัดการคําขอเครือข่ายที่ล้มเหลว

และสุดท้าย เราจะต้องจัดการเหตุการณ์ fetch เราจะใช้กลยุทธ์เครือข่ายสํารองเพื่อแคช Service Worker พยายามดึงข้อมูลทรัพยากรจากเครือข่ายก่อน หากไม่สําเร็จ Service Worker จะแสดงหน้าแบบออฟไลน์จากแคช

public/service-worker.js

// CODELAB: Add fetch event handler here.
if (evt.request.mode !== 'navigate') {
  // Not a page navigation, bail.
  return;
}
evt.respondWith(
    fetch(evt.request)
        .catch(() => {
          return caches.open(CACHE_NAME)
              .then((cache) => {
                return cache.match('offline.html');
              });
        })
);

เครื่องจัดการ fetch ต้องจัดการเฉพาะการนําทางของหน้าเท่านั้น คําขออื่นๆ จึงถูกปล่อยออกจากเครื่องจัดการและจัดการด้วยเบราว์เซอร์ตามปกติ แต่หากคําขอ .mode คือ navigate ให้ใช้ fetch เพื่อพยายามรับรายการจากเครือข่าย หากไม่สําเร็จ เครื่องจัดการ catch จะเปิดแคชด้วย caches.open(CACHE_NAME) และใช้ cache.match('offline.html') เพื่อรับหน้าออฟไลน์ที่แคชไว้ล่วงหน้า จากนั้นระบบจะส่งผลลัพธ์กลับไปยังเบราว์เซอร์โดยใช้ evt.respondWith()

เปลี่ยนเส้นทางเครื่องมือสําหรับนักพัฒนาเว็บ

มาลองตรวจสอบว่าทุกอย่างทํางานตามที่คาดไว้ เมื่อแผง Service Worker เปิดอยู่ ให้รีเฟรชหน้า คุณจะเห็น Service Worker ใหม่และหมายเลขสถานะเพิ่มขึ้น

นอกจากนี้ เราตรวจสอบได้ว่ามีการแคชอะไรไว้บ้าง ไปที่แผงพื้นที่เก็บข้อมูลแคชในแผงแอปพลิเคชันของ DevTools คลิกขวาที่ Storage Storage, เลือก Refresh Caches และขยายส่วนนั้น คุณจะเห็นชื่อของแคชแบบคงที่ที่แสดงทางด้านซ้าย คลิกชื่อแคชเพื่อดูไฟล์ทั้งหมดที่แคชไว้

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

โหลดหน้าเว็บซ้ําและ... ใช้งานได้ เราได้เอาแพนด้าออฟไลน์มาแทนไดโนเสาร์ออฟไลน์ของ Chrome

เคล็ดลับสําหรับการทดสอบ Service Worker

การแก้ไขข้อบกพร่องของ Service Worker อาจเป็นเรื่องท้าทาย และเมื่อมีแคชสําหรับการแคช สิ่งต่างๆ จะยิ่งอยู่ในหน่วยความจํามากขึ้นหากแคชไม่อัปเดตตามที่คุณคาดไว้ ระหว่างวงจรการทํางานทั่วไปของบริการและข้อบกพร่องในโค้ด คุณอาจรู้สึกหงุดหงิดได้อย่างรวดเร็ว แต่ไม่ใช่

ใช้เครื่องมือสําหรับนักพัฒนาเว็บ

ในแผง Service Worker ของแผงแอปพลิเคชันจะมีช่องทําเครื่องหมาย 2-3 ช่องที่จะช่วยให้ชีวิตของคุณง่ายขึ้น

  • ออฟไลน์ - เมื่อเลือกตัวเลือกนี้ เลียนแบบประสบการณ์แบบออฟไลน์และป้องกันไม่ให้คําขอใดๆ ถูกนําไปยังเครือข่าย
  • อัปเดตเมื่อโหลดซ้ํา - เมื่อเลือกช่องนี้ ผู้ใช้จะได้รับ Service Worker ล่าสุด ติดตั้ง และเปิดใช้งานทันที
  • การข้ามสําหรับเครือข่าย - เมื่อเลือก คําขอจะข้าม Service Worker และส่งไปยังเครือข่ายโดยตรง

เริ่มต้นใหม่

ในบางกรณีคุณอาจพบว่ากําลังโหลดข้อมูลที่แคชไว้หรือไม่มีข้อมูลที่อัปเดตตามที่คาดไว้ หากต้องการล้างข้อมูลที่บันทึกไว้ทั้งหมด (localStorage, ข้อมูล indexDB, ไฟล์ที่แคชไว้) และนํา Service Worker ออก ให้ใช้แผงล้างพื้นที่เก็บข้อมูลในแผงแอปพลิเคชัน หรือจะทํางานในหน้าต่างที่ไม่ระบุตัวตนก็ได้เช่นกัน

เคล็ดลับเพิ่มเติม:

  • เมื่อยกเลิกการลงทะเบียน Service Worker แล้ว นโยบายนั้นอาจปรากฏอยู่ในรายการจนกว่าจะปิดหน้าต่างเบราว์เซอร์ที่มี
  • หากหน้าต่างหลายบานไปยังแอปของคุณเปิดอยู่ Service Worker ใหม่จะไม่มีผลจนกว่าจะโหลดหน้าต่างทั้งหมดซ้ําและอัปเดตเป็น Service Worker ล่าสุด
  • การยกเลิกการลงทะเบียน Service Worker ไม่ได้ล้างแคช!
  • หากมี Service Worker อยู่และมีการลงทะเบียน Service Worker ใหม่ Service Worker ใหม่จะควบคุมไม่ได้จนกว่าจะโหลดหน้านี้ซ้ํา เว้นแต่คุณจะควบคุมโดยทันที

ยืนยันการเปลี่ยนแปลงด้วย Lighthouse

เรียกใช้ Lighthouse อีกครั้งและยืนยันการเปลี่ยนแปลง อย่าลืมยกเลิกการเลือกช่องทําเครื่องหมายออฟไลน์ก่อนยืนยันการเปลี่ยนแปลง

การตรวจสอบ SEO

  • 📲 ผ่าน: เอกสารมีคําอธิบายเมตา

Progressive Web App Audit

  • 📲 ผ่าน: หน้าปัจจุบันจะตอบกลับด้วย 200 เมื่อออฟไลน์
  • แห่งนี้ตั้งอยู่ ผ่าน: start_url ตอบสนองด้วยรหัส 200 เมื่อออฟไลน์
  • ⚠ ผ่าน: ลงทะเบียน Service Worker ที่ควบคุมหน้าและ start_url.
  • 📲 ผ่าน: ไฟล์ Manifest ของเว็บแอปเป็นไปตามข้อกําหนดด้านความสามารถในการติดตั้ง
  • 📲 ผ่าน: กําหนดค่าสําหรับหน้าจอแนะนําที่กําหนดเอง
  • ⚠ ผ่าน: ตั้งค่าสีธีมของแถบที่อยู่

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

วงจรการทํางานของ Service Worker

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

install กิจกรรม

เหตุการณ์แรกที่ Service Worker ได้รับคือ install ระบบจะเรียกให้ทํางานทันทีที่ผู้ปฏิบัติงานทํางาน รวมทั้งจะโทรหาเพียงครั้งเดียวต่อผู้ปฏิบัติงานบริการ หากเปลี่ยนสคริปต์ Service Worker เบราว์เซอร์จะถือว่าเป็น Service Worker อื่น และจะได้รับเหตุการณ์ install ของตัวเอง

โดยทั่วไปเหตุการณ์ install จะใช้เพื่อแคชทุกอย่างที่จําเป็นเพื่อให้แอปทํางาน

activate กิจกรรม

Service Worker จะได้รับเหตุการณ์ activate ทุกครั้งที่เริ่มทํางาน วัตถุประสงค์หลักของเหตุการณ์ activate คือการกําหนดค่าลักษณะการทํางานของ Service Worker, ล้างทรัพยากรที่ค้างอยู่จากการเรียกใช้ก่อนหน้า (เช่น แคชเก่า) และเตรียม Service Worker ให้จัดการคําขอเครือข่าย (เช่น เหตุการณ์ fetch ที่อธิบายไว้ด้านล่าง)

fetch กิจกรรม

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

การอัปเดต Service Worker

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

การเลือกกลยุทธ์การแคชที่เหมาะสม

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

การแคชทรัพยากรแบบคงที่

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

สําหรับแอปของเรา เราจะแคชทรัพยากรแบบคงที่ทั้งหมดไว้ล่วงหน้าเมื่อมีการติดตั้ง Service Worker เพื่อให้ทุกอย่างที่เราต้องการสําหรับเรียกใช้แอปจัดเก็บอยู่ในอุปกรณ์ของผู้ใช้ เพื่อให้แน่ใจว่าแอปจะโหลดได้อย่างรวดเร็ว เราจะใช้กลยุทธ์ cache-first แทนที่จะไปที่เครือข่ายเพื่อดึงทรัพยากร แหล่งข้อมูลจะดึงจากแคชในเครื่องเท่านั้น (ถ้าใช้งานไม่ได้) เราจะพยายามดึงข้อมูลจากเครือข่าย

การดึงแคชในเครื่องจะลบความแปรปรวนของเครือข่ายออก ไม่ว่าผู้ใช้จะใช้เครือข่ายประเภทใด (WiFi, 5G, 3G หรือแม้แต่ 2G) ทรัพยากรสําคัญที่เราต้องเรียกใช้ก็จะพร้อมใช้งานแทบจะในทันที

การแคชข้อมูลแอป

กลยุทธ์ไม่มีการตรวจสอบความถูกต้องอีกครั้งเหมาะสําหรับข้อมูลบางประเภทและทํางานได้ดีสําหรับแอปของเรา โดยจะได้รับข้อมูลบนหน้าจอโดยเร็วที่สุดเท่าที่จะเป็นไปได้ แล้วอัปเดตเมื่อเครือข่ายแสดงข้อมูลล่าสุด การตรวจสอบที่ไม่มีการอัปเดตอีกครั้งหมายความว่าเราต้องเริ่มคําขอแบบอะซิงโครนัส 2 รายการ โดยคําขอรายการหนึ่งส่งไปยังแคชและอีกคําขอไปยังเครือข่าย

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

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

อัปเดตตรรกะของแอป

ตามที่ได้กล่าวไปแล้ว แอปจะต้องเริ่มคําขอแบบอะซิงโครนัส 2 รายการ โดยคําขอหนึ่งส่งไปยังแคชและอีกรายการไปยังเครือข่าย แอปใช้ออบเจ็กต์ caches ที่มีอยู่ใน window เพื่อเข้าถึงแคชและเรียกข้อมูลล่าสุด นี่เป็นตัวอย่างการเพิ่มประสิทธิภาพแบบต่อเนื่องที่ยอดเยี่ยม เพราะออบเจ็กต์ caches อาจไม่พร้อมใช้งานในบางเบราว์เซอร์ และหากไม่พร้อมใช้งาน คําขอเครือข่ายควรใช้งานได้

อัปเดตฟังก์ชัน getForecastFromCache() เพื่อตรวจสอบว่าวัตถุ caches พร้อมใช้งานในออบเจ็กต์ window ส่วนกลางหรือไม่ และหากใช่ ให้ขอข้อมูลจากแคช

public/scripts/app.js

// CODELAB: Add code to get weather forecast from the caches object.
if (!('caches' in window)) {
  return null;
}
const url = `${window.location.origin}/forecast/${coords}`;
return caches.match(url)
    .then((response) => {
      if (response) {
        return response.json();
      }
      return null;
    })
    .catch((err) => {
      console.error('Error getting data from cache', err);
      return null;
    });

จากนั้นเราจําเป็นต้องแก้ไข updateData() เพื่อให้เรียกใช้ 2 ครั้ง โดยครั้งหนึ่งเป็น getForecastFromNetwork() เพื่อรับการคาดการณ์จากเครือข่าย และครั้งที่ 2 เป็น getForecastFromCache() เพื่อรับการคาดการณ์ที่แคชไว้ล่าสุด

public/scripts/app.js

// CODELAB: Add code to call getForecastFromCache.
getForecastFromCache(location.geo)
    .then((forecast) => {
      renderForecast(card, forecast);
    });

ตอนนี้แอปสภาพอากาศของเราส่งคําขอข้อมูลแบบไม่พร้อมกัน 2 รายการ คําขอหนึ่งมาจากแคชและคําขอผ่าน fetch หากมีข้อมูลในแคช ระบบจะส่งคืนและแสดงผลอย่างรวดเร็วมาก (หลายมิลลิวินาที) จากนั้นเมื่อ fetch ตอบสนอง ระบบจะอัปเดตการ์ดด้วยข้อมูลใหม่ล่าสุดจากสภาพอากาศใน API โดยตรง

โปรดสังเกตว่าคําขอแคชและคําขอ fetch ลงท้ายด้วยการเรียกเพื่ออัปเดตการ์ดการคาดการณ์ แอปจะรู้ได้อย่างไรว่าแอปแสดงข้อมูลล่าสุด ซึ่งจะจัดการในโค้ดต่อไปนี้จาก renderForecast()

public/scripts/app.js

// If the data on the element is newer, skip the update.
if (lastUpdated >= data.currently.time) {
  return;
}

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

แคชทรัพยากรของแอปไว้ล่วงหน้า

ใน Service Worker เราจะมาเพิ่ม DATA_CACHE_NAME เพื่อให้เราแยกข้อมูลแอปพลิเคชันของเราจาก App Shell ได้ เมื่ออัปเดต Shell ของแอปและแคชเก่าถูกลบออกถาวร ข้อมูลของเราจะยังคงอยู่โดยไม่มีการเปลี่ยนแปลง ซึ่งพร้อมสําหรับการโหลดที่รวดเร็วมาก โปรดทราบว่าหากรูปแบบข้อมูลมีการเปลี่ยนแปลงในอนาคต คุณจะต้องจัดการวิธีจัดการและตรวจสอบว่า Shell และเนื้อหาซิงค์กันอยู่เสมอ

public/service-worker.js

// CODELAB: Update cache names any time any of the cached files change.
const CACHE_NAME = 'static-cache-v2';
const DATA_CACHE_NAME = 'data-cache-v1';

อย่าลืมอัปเดต CACHE_NAME ด้วยและเราจะเปลี่ยนทรัพยากรแบบคงที่ทั้งหมดด้วย

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

อัปเดตอาร์เรย์ FILES_TO_CACHE ด้วยรายการไฟล์

public/service-worker.js

// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
  '/',
  '/index.html',
  '/scripts/app.js',
  '/scripts/install.js',
  '/scripts/luxon-1.11.4.js',
  '/styles/inline.css',
  '/images/add.svg',
  '/images/clear-day.svg',
  '/images/clear-night.svg',
  '/images/cloudy.svg',
  '/images/fog.svg',
  '/images/hail.svg',
  '/images/install.svg',
  '/images/partly-cloudy-day.svg',
  '/images/partly-cloudy-night.svg',
  '/images/rain.svg',
  '/images/refresh.svg',
  '/images/sleet.svg',
  '/images/snow.svg',
  '/images/thunderstorm.svg',
  '/images/tornado.svg',
  '/images/wind.svg',
];

เนื่องจากเราจะสร้างรายการไฟล์ที่จะแคชด้วยตนเอง ทุกครั้งที่อัปเดตไฟล์ เราจึงต้องอัปเดต CACHE_NAME เราสามารถนํา offline.html ออกจากรายการไฟล์ที่แคชได้เนื่องจากแอปของเรามีทรัพยากรที่จําเป็นทั้งหมดสําหรับทํางานแบบออฟไลน์ และจะไม่แสดงหน้าเว็บออฟไลน์อีกครั้ง

อัปเดตเครื่องจัดการเหตุการณ์การเปิดใช้งาน

เพื่อให้แน่ใจว่ากิจกรรม activate ของเราจะไม่ลบข้อมูลของเราโดยไม่ตั้งใจ ให้แทนที่ if (key !== CACHE_NAME) { ในเหตุการณ์ activate ด้วย service-worker.js:

public/service-worker.js

if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {

อัปเดตเครื่องจัดการเหตุการณ์การดึงข้อมูล

เราจําเป็นต้องแก้ไข Service Worker เพื่อสกัดกั้นคําขอจาก API สภาพอากาศและจัดเก็บการตอบกลับไว้ในแคช เพื่อให้เข้าถึงได้อย่างง่ายดายในภายหลัง ในกลยุทธ์การอัปเดตที่ไม่อัปเดต เราคาดหวังว่าการตอบสนองของเครือข่ายจะเป็น "แหล่งที่มาที่เชื่อถือได้"&#39 ให้ข้อมูลล่าสุดแก่เราเสมอ หากเครือข่ายทําไม่ได้ก็ไม่สําเร็จเนื่องจากเราดึงข้อมูลที่แคชไว้ล่าสุดในแอปแล้ว

อัปเดตเครื่องจัดการเหตุการณ์ fetch เพื่อจัดการคําขอไปยัง API ข้อมูลแยกต่างหากจากคําขออื่นๆ

public/service-worker.js

// CODELAB: Add fetch event handler here.
if (evt.request.url.includes('/forecast/')) {
  console.log('[Service Worker] Fetch (data)', evt.request.url);
  evt.respondWith(
      caches.open(DATA_CACHE_NAME).then((cache) => {
        return fetch(evt.request)
            .then((response) => {
              // If the response was good, clone it and store it in the cache.
              if (response.status === 200) {
                cache.put(evt.request.url, response.clone());
              }
              return response;
            }).catch((err) => {
              // Network request failed, try to get it from the cache.
              return cache.match(evt.request);
            });
      }));
  return;
}
evt.respondWith(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.match(evt.request)
          .then((response) => {
            return response || fetch(evt.request);
          });
    })
);

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

เราต้องนําการตรวจสอบ evt.request.mode !== 'navigate' ออก เนื่องจากเราต้องการให้ Service Worker จัดการคําขอทั้งหมด (รวมถึงรูปภาพ สคริปต์ ไฟล์ CSS ฯลฯ) ไม่ใช่แค่การนําทางเท่านั้น หากปล่อยเช็คอินไว้ คุณจะเห็นเฉพาะ HTML จากแคชของ Service Worker คําขออื่นๆ จะขอจากเครือข่าย

ลองเลย

แอปควรทํางานได้ทํางานแบบออฟไลน์อย่างสมบูรณ์แล้วในขณะนี้ รีเฟรชหน้านี้เพื่อให้แน่ใจว่าได้ติดตั้ง Service Worker ล่าสุดแล้ว จากนั้นบันทึกเมือง 2 เมืองและกดปุ่มรีเฟรชในแอปเพื่อดูข้อมูลสภาพอากาศใหม่

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

เปลี่ยนเป็นแผงผู้ปฏิบัติงานบริการ และเลือกช่องทําเครื่องหมายออฟไลน์ ลองโหลดหน้านี้ซ้ํา แล้วออฟไลน์แล้วโหลดหน้าเว็บอีกครั้ง

หากคุณใช้เครือข่ายที่เร็วและต้องการดูข้อมูลการอัปเดตพยากรณ์อากาศผ่านการเชื่อมต่อที่มีความเร็วต่ํา ให้ตั้งค่าพร็อพเพอร์ตี้ FORECAST_DELAY ใน server.js เป็น 5000 คําขอทั้งหมดสําหรับ API การคาดการณ์จะล่าช้า 5,000 มิลลิวินาที

ยืนยันการเปลี่ยนแปลงด้วย Lighthouse

การเรียกใช้ Lighthouse อีกครั้งก็เป็นความคิดที่ดี

การตรวจสอบ SEO

  • 📲 ผ่าน: เอกสารมีคําอธิบายเมตา

Progressive Web App Audit

  • 📲 ผ่าน: หน้าปัจจุบันจะตอบกลับด้วย 200 เมื่อออฟไลน์
  • แห่งนี้ตั้งอยู่ ผ่าน: start_url ตอบสนองด้วยรหัส 200 เมื่อออฟไลน์
  • ⚠ ผ่าน: ลงทะเบียน Service Worker ที่ควบคุมหน้าและ start_url.
  • 📲 ผ่าน: ไฟล์ Manifest ของเว็บแอปเป็นไปตามข้อกําหนดด้านความสามารถในการติดตั้ง
  • 📲 ผ่าน: กําหนดค่าสําหรับหน้าจอแนะนําที่กําหนดเอง
  • ⚠ ผ่าน: ตั้งค่าสีธีมของแถบที่อยู่

เมื่อติดตั้ง Progressive Web App แล้ว ลักษณะการทํางานและมีลักษณะเหมือนกับแอปอื่นๆ ที่ติดตั้งไว้ทั้งหมด โดยจะเปิดจากที่เดียวกันกับแอปอื่นๆ ซึ่งจะทํางานในแอปโดยไม่มีแถบที่อยู่หรือ UI อื่นๆ ของเบราว์เซอร์ เช่นเดียวกับแอปที่ติดตั้งไว้อื่นๆ แอปนี้จะเป็นแอประดับสูงสุดในตัวสลับงาน

ใน Chrome สามารถติดตั้ง Progressive Web App ผ่านเมนูตามบริบท 3 จุด หรือคุณจะระบุปุ่มหรือคอมโพเนนต์ UI อื่นๆ ที่จะแจ้งให้ผู้ใช้ติดตั้งแอปก็ได้

การตรวจสอบด้วย Lighthouse

ผู้ใช้ต้องปฏิบัติตามเกณฑ์ที่กําหนดเพื่อให้ผู้ใช้ติดตั้ง Progressive Web App ได้ วิธีที่ง่ายที่สุดในการตรวจสอบคือการใช้ Lighthouse และตรวจสอบว่าอุปกรณ์เป็นไปตามเกณฑ์ที่ติดตั้งได้

หากคุณใช้ Codelab นี้แล้ว PWA ของคุณก็ควรเป็นไปตามเกณฑ์เหล่านี้แล้ว

เพิ่ม install.js ไปยัง index.html

ขั้นแรก เราจะเพิ่ม install.js ลงในไฟล์ index.html

public/index.html

<!-- CODELAB: Add the install script here -->
<script src="/scripts/install.js"></script>

ฟัง beforeinstallprompt กิจกรรม

หากตรงตามเกณฑ์ของหน้าจอหลักแล้ว Chrome จะทําให้เหตุการณ์ beforeinstallprompt เริ่มทํางานเพื่อระบุว่าแอปของคุณเป็นแบบได้ 'installed&#39 แล้วแจ้งให้ผู้ใช้ติดตั้ง เพิ่มโค้ดด้านล่างเพื่อฟังเหตุการณ์ beforeinstallprompt

public/scripts/install.js

// CODELAB: Add event listener for beforeinstallprompt event
window.addEventListener('beforeinstallprompt', saveBeforeInstallPromptEvent);

บันทึกกิจกรรมและแสดงปุ่มติดตั้ง

ในฟังก์ชัน saveBeforeInstallPromptEvent เราจะบันทึกการอ้างอิงไปยังเหตุการณ์ beforeinstallprompt เพื่อให้เราเรียกใช้ prompt() ในภายหลัง และอัปเดต UI ให้แสดงปุ่มติดตั้งได้

public/scripts/install.js

// CODELAB: Add code to save event & show the install button.
deferredInstallPrompt = evt;
installButton.removeAttribute('hidden');

แสดงข้อความแจ้งและซ่อนปุ่ม

เมื่อผู้ใช้คลิกปุ่มติดตั้ง เราต้องเรียกใช้ .prompt() ในเหตุการณ์ beforeinstallprompt ที่บันทึกไว้ และเราต้องซ่อนปุ่มติดตั้งด้วย เพราะเรียกใช้ .prompt() ได้เพียง 1 ครั้งในแต่ละเหตุการณ์ที่บันทึกไว้

public/scripts/install.js

// CODELAB: Add code show install prompt & hide the install button.
deferredInstallPrompt.prompt();
// Hide the install button, it can't be called twice.
evt.srcElement.setAttribute('hidden', true);

การโทร .prompt() จะแสดงกล่องโต้ตอบโมดัลแก่ผู้ใช้ ขอให้ผู้ใช้เพิ่มแอปลงในหน้าจอหลัก

บันทึกผลลัพธ์

คุณตรวจสอบได้ว่าผู้ใช้ตอบสนองต่อกล่องโต้ตอบการติดตั้งอย่างไรด้วยการฟังคํามั่นสัญญาที่ส่งโดยพร็อพเพอร์ตี้ userChoice ของเหตุการณ์ beforeinstallprompt ที่บันทึกไว้ การสัญญาจะแสดงออบเจ็กต์ที่มีพร็อพเพอร์ตี้ outcome หลังจากที่ระบบแสดงข้อความแจ้งและผู้ใช้ตอบกลับแล้ว

public/scripts/install.js

// CODELAB: Log user response to prompt.
deferredInstallPrompt.userChoice
    .then((choice) => {
      if (choice.outcome === 'accepted') {
        console.log('User accepted the A2HS prompt', choice);
      } else {
        console.log('User dismissed the A2HS prompt', choice);
      }
      deferredInstallPrompt = null;
    });

ความคิดเห็นเกี่ยวกับ userChoice: ข้อกําหนดกําหนดให้เป็นพร็อพเพอร์ตี้ ไม่ใช่ฟังก์ชันตามที่คาดไว้

บันทึกเหตุการณ์การติดตั้งทั้งหมด

นอกจาก UI ที่คุณเพิ่มเพื่อติดตั้งแอปแล้ว ผู้ใช้ยังติดตั้ง PWA ด้วยวิธีอื่นๆ ได้ เช่น เมนู 3 จุดของ Chrome หากต้องการติดตามเหตุการณ์เหล่านี้ ให้ฟังเหตุการณ์ที่มีการติดตั้งแอป

public/scripts/install.js

// CODELAB: Add event listener for appinstalled event
window.addEventListener('appinstalled', logAppInstalled);

จากนั้นเราจําเป็นต้องอัปเดตฟังก์ชัน logAppInstalled สําหรับ Codelab นี้ เราจะใช้ console.log แต่ในแอปเวอร์ชันที่ใช้งานจริง คุณอาจต้องการบันทึกเป็นเหตุการณ์ด้วยซอฟต์แวร์การวิเคราะห์ของคุณ

public/scripts/install.js

// CODELAB: Add code to log the event
console.log('Weather App was installed.', evt);

อัปเดต Service Worker

อย่าลืมอัปเดต CACHE_NAME ในไฟล์ service-worker.js เมื่อคุณเปลี่ยนไฟล์ที่แคชไว้ไปแล้ว การเปิดใช้ช่องทําเครื่องหมายการข้ามสําหรับเครือข่ายในแผง Service Worker ของแผงแอปพลิเคชันในเครื่องมือสําหรับนักพัฒนาเว็บจะทํางานในการพัฒนาได้ แต่จะไม่เป็นประโยชน์ต่อการใช้งานจริง

ลองเลย

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

ยืนยันว่าปุ่มติดตั้งแสดงอยู่

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

  1. เปิด URL ในแท็บ Chrome ใหม่
  2. เปิดเมนู 3 จุดของ Chrome (ข้างแถบที่อยู่)
    ▢ ยืนยันว่าคุณเห็น "ติดตั้งสภาพอากาศ..." ในเมนู
  3. รีเฟรชข้อมูลสภาพอากาศโดยใช้ปุ่มรีเฟรชที่มุมขวาบนเพื่อให้มั่นใจว่าเราเป็นไปตามวิธีการมีส่วนร่วมของผู้ใช้
    ▢ ตรวจสอบว่าไอคอนติดตั้งแสดงในส่วนหัวของแอป

ยืนยันว่าปุ่มติดตั้งใช้งานได้

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

  1. เปิด Chrome และในแท็บใหม่ของเบราว์เซอร์ ให้ไปที่ Weather PWA
  2. เปิดเครื่องมือสําหรับนักพัฒนาเว็บและสลับไปที่แผงคอนโซล
  3. คลิกปุ่มติดตั้งที่มุมบนขวา
    ▢ ตรวจสอบว่าปุ่มติดตั้งหายไป
    ▢ ยืนยันว่ากล่องโต้ตอบโมดัลติดตั้งปรากฏขึ้น
  4. คลิกยกเลิก
    ▢ ยืนยัน "ผู้ใช้ปิดข้อความแจ้ง A2HS" ปรากฏในเอาต์พุตของคอนโซล
    ▢ ยืนยันปุ่มติดตั้งอีกครั้ง
  5. คลิกปุ่มติดตั้งอีกครั้ง แล้วคลิกปุ่มติดตั้งในกล่องโต้ตอบโมดัล
    ▢ ยืนยัน "ผู้ใช้ยอมรับข้อความแจ้ง A2HS" แสดงอยู่ในเอาต์พุตของคอนโซล
    ▢ ยืนยัน "ติดตั้งแอปสภาพอากาศ" จะแสดงในเอาต์พุตของคอนโซล
    ▢ ตรวจสอบว่ามีการเพิ่มแอปสภาพอากาศลงในตําแหน่งที่คุณมักจะพบแอป
  6. เปิด Weather PWA
    ▢ ตรวจสอบว่าแอปเปิดเป็นแอปแบบสแตนด์อโลนได้ทั้งในหน้าต่างแอปบนเดสก์ท็อปหรือแบบเต็มหน้าจอบนอุปกรณ์เคลื่อนที่

.

ยืนยันว่าการติดตั้ง iOS ทํางานอย่างถูกต้อง

มาตรวจสอบพฤติกรรมใน iOS กัน หากคุณมีอุปกรณ์ iOS คุณสามารถใช้อุปกรณ์ดังกล่าวได้ หรือหากใช้งาน Mac ให้ลองใช้เครื่องจําลอง iOS ที่ใช้ได้กับ Xcode

  1. เปิด Safari และในแท็บใหม่ของเบราว์เซอร์ ให้ไปที่ Weather PWA
  2. คลิกปุ่มแชร์
  3. เลื่อนไปทางขวาและคลิกปุ่มเพิ่มลงในหน้าจอหลัก
    ▢ ยืนยันว่าชื่อ, URL และไอคอนถูกต้อง
  4. คลิกเพิ่ม
    ▢ ตรวจสอบว่าได้เพิ่มไอคอนแอปลงในหน้าจอหลักแล้ว
  5. เปิด Peather PWA จากหน้าจอหลัก
    ▢ ตรวจสอบว่าแอปเปิดเต็มหน้าจอ

โบนัส: การตรวจสอบว่าแอปเปิดแล้วในหน้าจอหลักหรือไม่

คําค้นหาสื่อ display-mode ช่วยให้คุณนําสไตล์ไปใช้ได้ ทั้งนี้ขึ้นอยู่กับวิธีการเปิดแอปหรือกําหนดวิธีเปิดตัวด้วย JavaScript

@media all and (display-mode: standalone) {
  body {
    background-color: yellow;
  }
}

ยังสามารถตรวจสอบการค้นหาสื่อ display-mode ใน JavaScript เพื่อดูว่ากําลังทํางานแบบสแตนด์อโลนหรือไม่

โบนัส: การถอนการติดตั้ง PWA

อย่าลืมว่า beforeinstallevent จะไม่เริ่มทํางานหากติดตั้งแอปไว้แล้ว ดังนั้นในระหว่างการพัฒนา คุณอาจต้องติดตั้งและถอนการติดตั้งแอปหลายๆ ครั้งเพื่อให้แน่ใจว่าทุกอย่างทํางานตามที่คาดไว้

Android

ส่วนใน Android จะถอนการติดตั้ง PWA ด้วยวิธีเดียวกับที่ติดตั้งแอปที่ติดตั้งไว้อื่นๆ

  1. เปิดลิ้นชักแอป
  2. เลื่อนลงเพื่อค้นหาไอคอนสภาพอากาศ
  3. ลากไอคอนแอปไปที่ด้านบนของหน้าจอ
  4. เลือกถอนการติดตั้ง

ChromeOS

ใน Chrome OS สามารถถอนการติดตั้ง PWA ได้โดยง่ายจากช่องค้นหาของ Launcher

  1. เปิด Launcher
  2. พิมพ์ "Weather" ลงในช่องค้นหา Peather ของ PWA ควรปรากฏในผลการค้นหา
  3. คลิกขวา (Alt-click) ใน Weather PWA
  4. คลิกนําออกจาก Chrome...

macOS และ Windows

ใน Mac และ Windows ระบบอาจถอนการติดตั้ง PWA ผ่าน Chrome

  1. เปิด chrome://apps ในแท็บใหม่ของเบราว์เซอร์
  2. คลิกขวา (Alt-click) ใน Weather PWA
  3. คลิกนําออกจาก Chrome...

นอกจากนี้คุณยังเปิด PWA ที่ติดตั้งไว้ แล้วคลิกเมนูตามบริบท 3 จุดที่มุมขวาบน แล้วเลือก "ถอนการติดตั้ง PWA..."

ยินดีด้วย คุณสร้าง Progressive Web App แรกเรียบร้อยแล้ว

คุณได้เพิ่มไฟล์ Manifest ของเว็บแอปเพื่อให้ติดตั้งได้และเพิ่ม Service Worker เพื่อให้มั่นใจว่า PWA รวดเร็วและเชื่อถือได้เสมอ คุณได้ดูวิธีใช้เครื่องมือสําหรับนักพัฒนาเว็บในการตรวจสอบแอป ตลอดจนวิธีปรับปรุงประสบการณ์ของผู้ใช้

ตอนนี้คุณทราบขั้นตอนสําคัญในการเปลี่ยนเว็บแอปใดๆ ให้เป็น Progressive Web App แล้ว

ขั้นตอนถัดไปที่ควรทํา

ลองดู Codelab เหล่านี้...

อ่านเพิ่มเติม

เอกสารอ้างอิง