คู่มือสำหรับผู้เริ่มต้นใช้งานแคชของแอปพลิเคชัน

เกริ่นนำ

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

  1. การท่องเว็บแบบออฟไลน์ - ผู้ใช้จะไปยังส่วนต่างๆ ของเว็บไซต์แบบเต็มได้เมื่อออฟไลน์
  2. ความเร็ว - ทรัพยากรจะมาจากดิสก์โดยตรง โดยไม่มีการเดินทางไปยังเครือข่าย
  3. ความยืดหยุ่น - หากเว็บไซต์หยุดทำงานเพื่อ "บำรุงรักษา" (เช่น มีคนทำทุกอย่างโดยไม่ตั้งใจ) ผู้ใช้ก็จะได้รับประสบการณ์แบบออฟไลน์

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

ไฟล์ Manifest ของแคช

ไฟล์ Manifest ของแคชเป็นไฟล์ข้อความอย่างง่ายที่แสดงทรัพยากรที่เบราว์เซอร์ควรแคชสำหรับการเข้าถึงแบบออฟไลน์

การอ้างอิงไฟล์ Manifest

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

<html manifest="example.appcache">
  ...
</html>

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

คุณสามารถดู URL ที่ควบคุมโดยแคชของแอปพลิเคชันได้โดยไปที่ about://appcache-internals/ ใน Chrome จากที่นี่ คุณสามารถล้างแคชและดูรายการได้ มีเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ที่คล้ายกันใน Firefox

แอตทริบิวต์ manifest สามารถชี้ไปยัง URL ที่สมบูรณ์หรือเส้นทางสัมพัทธ์ได้ แต่ URL ที่สมบูรณ์ต้องอยู่ภายใต้ต้นทางเดียวกันกับเว็บแอปพลิเคชัน ไฟล์ Manifest สามารถมีนามสกุลไฟล์ใดก็ได้ แต่ต้องแสดงด้วยประเภท MIME ที่ถูกต้อง (ดูด้านล่าง)

<html manifest="http://www.example.com/example.mf">
  ...
</html>

ไฟล์ Manifest ต้องแสดงด้วยประเภท MIME text/cache-manifest คุณอาจต้องเพิ่มประเภทไฟล์ที่กำหนดเองไปยังเว็บเซิร์ฟเวอร์หรือการกำหนดค่า .htaccess

ตัวอย่างเช่น หากต้องการแสดงประเภท MIME นี้ใน Apache ให้เพิ่มบรรทัดต่อไปนี้ลงในไฟล์การกำหนดค่า

AddType text/cache-manifest .appcache

หรือในไฟล์ app.yaml ใน Google App Engine ให้ทำดังนี้

- url: /mystaticdir/(.*\.appcache)
  static_files: mystaticdir/\1
  mime_type: text/cache-manifest
  upload: mystaticdir/(.*\.appcache)

ข้อกำหนดนี้ยกเลิกไปตามข้อกำหนดเมื่อนานมาแล้ว และ Chrome, Safari และ Firefox เวอร์ชันล่าสุดไม่ได้บังคับอีกต่อไป แต่คุณจะต้องใช้ประเภท MIME จึงจะทำงานได้ในเบราว์เซอร์รุ่นเก่าและ IE11

โครงสร้างของไฟล์ Manifest

ไฟล์ Manifest เป็นไฟล์แยกต่างหากที่คุณลิงก์ผ่านแอตทริบิวต์ไฟล์ Manifest ในองค์ประกอบ HTML ไฟล์ Manifest แบบง่ายจะมีลักษณะดังนี้

CACHE MANIFEST
index.html
stylesheet.css
images/logo.png
scripts/main.js
http://cdn.example.com/scripts/main.js

ตัวอย่างนี้จะแคช 4 ไฟล์ในหน้าที่ระบุไฟล์ Manifest นี้

สิ่งที่ควรทราบมีดังนี้

  • สตริง CACHE MANIFEST คือบรรทัดแรกและต้องระบุ
  • ไฟล์อาจมาจากโดเมนอื่น
  • บางเบราว์เซอร์มีข้อจำกัดเกี่ยวกับปริมาณโควต้าพื้นที่เก็บข้อมูลที่แอปของคุณมี เช่น AppCache จะใช้พูลที่แชร์ของพื้นที่เก็บข้อมูลชั่วคราวซึ่ง API ออฟไลน์อื่นๆ สามารถแชร์ได้ หากคุณเขียนแอปสำหรับ Chrome เว็บสโตร์ การใช้ unlimitedStorage จะนำข้อจำกัดดังกล่าวออก
  • หากไฟล์ Manifest แสดงผล 404 หรือ 410 แคชจะถูกลบ
  • หากดาวน์โหลดไฟล์ Manifest หรือทรัพยากรที่ระบุไว้ในไฟล์ไม่ได้ กระบวนการอัปเดตแคชทั้งหมดจะล้มเหลว เบราว์เซอร์จะใช้แคชของแอปพลิเคชันเก่าต่อไปในกรณีที่เกิดข้อผิดพลาด

มาดูตัวอย่างที่ซับซ้อนมากขึ้นกัน

CACHE MANIFEST
# 2010-06-18:v2

# Explicitly cached 'master entries'.
CACHE:
/favicon.ico
index.html
stylesheet.css
images/logo.png
scripts/main.js

# Resources that require the user to be online.
NETWORK:
*

# static.html will be served if main.py is inaccessible
# offline.jpg will be served in place of all images in images/large/
# offline.html will be served in place of all other .html files
FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg

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

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

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

ไฟล์ Manifest สามารถมีส่วนที่แตกต่างกัน 3 ส่วน ได้แก่ CACHE, NETWORK และ FALLBACK

CACHE:
ส่วนนี้เป็นส่วนเริ่มต้นสำหรับรายการ ไฟล์ในรายการภายใต้ส่วนหัวนี้ (หรือทันทีหลังจาก CACHE MANIFEST) จะได้รับการแคชไว้อย่างชัดแจ้งหลังจากดาวน์โหลดเป็นครั้งแรก NETWORK:
ไฟล์ที่แสดงในส่วนนี้อาจมาจากเครือข่ายหากไฟล์เหล่านั้นไม่อยู่ในแคช มิเช่นนั้นจะไม่มีการใช้เครือข่ายดังกล่าว แม้ว่าผู้ใช้จะออนไลน์อยู่ก็ตาม คุณกำหนด URL ที่เจาะจงลงในรายการที่อนุญาตพิเศษได้ที่นี่ หรือแค่ "" ซึ่งอนุญาต URL ทั้งหมด เว็บไซต์ส่วนใหญ่ต้องมี "" FALLBACK:
ส่วนที่ไม่บังคับจะระบุหน้าสำรองหากทรัพยากรไม่สามารถเข้าถึงได้ URI แรกคือทรัพยากร ส่วน URL ที่ 2 คือ URL สำรองที่ใช้ในกรณีที่คำขอเครือข่ายล้มเหลวหรือมีข้อผิดพลาด URI ทั้ง 2 รายการต้องมาจากต้นทางเดียวกับไฟล์ Manifest คุณสามารถบันทึก URL ที่เจาะจง แต่ยังรวมถึงคำนำหน้า URL ได้ด้วย "images/large/" จะบันทึกความล้มเหลวจาก URL เช่น "images/large/whatever/img.jpg"

ไฟล์ Manifest ต่อไปนี้กำหนดหน้า "รับทั้งหมด" (offline.html) ที่จะแสดงเมื่อผู้ใช้พยายามเข้าถึงรากของเว็บไซต์ขณะออฟไลน์ และยังประกาศด้วยว่าทรัพยากรอื่นๆ ทั้งหมด (เช่น ทรัพยากรที่อยู่ในเว็บไซต์จากระยะไกล) ต้องมีการเชื่อมต่ออินเทอร์เน็ต

CACHE MANIFEST
# 2010-06-18:v3

# Explicitly cached entries
index.html
css/style.css

# offline.html will be displayed if the user is offline
FALLBACK:
/ /offline.html

# All other resources (e.g. sites) require the user to be online.
NETWORK:
*

# Additional resources to cache
CACHE:
images/logo1.png
images/logo2.png
images/logo3.png

กำลังอัปเดตแคช

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

  1. ผู้ใช้ล้างพื้นที่เก็บข้อมูลของเบราว์เซอร์สำหรับเว็บไซต์ของคุณ
  2. แก้ไขไฟล์ Manifest แล้ว หมายเหตุ: การอัปเดตไฟล์ที่แสดงในไฟล์ Manifest ไม่ได้หมายความว่าเบราว์เซอร์จะแคชทรัพยากรนั้นอีกครั้ง ตัวไฟล์ Manifest ต้องมีการแก้ไข

สถานะแคช

ออบเจ็กต์ window.applicationCache คือการเข้าถึงแคชของแอปของเบราว์เซอร์แบบเป็นโปรแกรม พร็อพเพอร์ตี้ status ของมีประโยชน์ในการตรวจสอบสถานะปัจจุบันของแคช

var appCache = window.applicationCache;

switch (appCache.status) {
case appCache.UNCACHED: // UNCACHED == 0
return 'UNCACHED';
break;
case appCache.IDLE: // IDLE == 1
return 'IDLE';
break;
case appCache.CHECKING: // CHECKING == 2
return 'CHECKING';
break;
case appCache.DOWNLOADING: // DOWNLOADING == 3
return 'DOWNLOADING';
break;
case appCache.UPDATEREADY:  // UPDATEREADY == 4
return 'UPDATEREADY';
break;
case appCache.OBSOLETE: // OBSOLETE == 5
return 'OBSOLETE';
break;
default:
return 'UKNOWN CACHE STATUS';
break;
};

หากต้องการตรวจหาการอัปเดตไฟล์ Manifest แบบเป็นโปรแกรม ให้เรียกใช้ applicationCache.update() ก่อน การดำเนินการนี้จะพยายามอัปเดตแคชของผู้ใช้ (ซึ่งต้องเปลี่ยนแปลงไฟล์ Manifest) สุดท้าย เมื่อ applicationCache.status อยู่ในสถานะ UPDATEREADY การเรียกใช้ applicationCache.swapCache() จะสลับแคชเก่าสำหรับแคชใหม่

var appCache = window.applicationCache;

appCache.update(); // Attempt to update the user's cache.

...

if (appCache.status == window.applicationCache.UPDATEREADY) {
appCache.swapCache();  // The fetch was successful, swap in the new cache.
}

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

// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {

window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
    // Browser downloaded a new app cache.
    if (confirm('A new version of this site is available. Load it?')) {
    window.location.reload();
    }
} else {
    // Manifest didn't changed. Nothing new to server.
}
}, false);

}, false);

เหตุการณ์ AppCache

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

function handleCacheEvent(e) {
//...
}

function handleCacheError(e) {
alert('Error: Cache failed to update!');
};

// Fired after the first cache of the manifest.
appCache.addEventListener('cached', handleCacheEvent, false);

// Checking for an update. Always the first event fired in the sequence.
appCache.addEventListener('checking', handleCacheEvent, false);

// An update was found. The browser is fetching resources.
appCache.addEventListener('downloading', handleCacheEvent, false);

// The manifest returns 404 or 410, the download failed,
// or the manifest changed while the download was in progress.
appCache.addEventListener('error', handleCacheError, false);

// Fired after the first download of the manifest.
appCache.addEventListener('noupdate', handleCacheEvent, false);

// Fired if the manifest file returns a 404 or 410.
// This results in the application cache being deleted.
appCache.addEventListener('obsolete', handleCacheEvent, false);

// Fired for each resource listed in the manifest as it is being fetched.
appCache.addEventListener('progress', handleCacheEvent, false);

// Fired when the manifest resources have been newly redownloaded.
appCache.addEventListener('updateready', handleCacheEvent, false);

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

รายการอ้างอิง