สรุป
ดูวิธีที่เราใช้ไลบรารี Service Worker เพื่อทําให้เว็บแอป Google I/O 2015 ทำงานได้อย่างรวดเร็วและเน้นการทำงานแบบออฟไลน์เป็นหลัก
ภาพรวม
เว็บแอป Google I/O 2015 ของปีนี้เขียนขึ้นโดยทีมนักพัฒนาซอฟต์แวร์สัมพันธ์ของ Google โดยอิงตามการออกแบบของเพื่อนๆ ใน Instrument ซึ่งเป็นผู้เขียนการทดลองเสียง/ภาพที่ยอดเยี่ยม ภารกิจของทีมเราคือการทำให้เว็บแอป I/O (ซึ่งเราจะเรียกด้วยชื่อรหัสว่า IOWA) แสดงทุกอย่างที่เว็บสมัยใหม่ทำได้ ประสบการณ์การใช้งานแบบออฟไลน์เป็นอันดับแรกคือฟีเจอร์ที่ต้องมี
หากคุณอ่านบทความอื่นๆ ในเว็บไซต์นี้เมื่อเร็วๆ นี้ คุณได้พบService Worker อย่างแน่นอน และคุณจะไม่แปลกใจเลยที่การสนับสนุนแบบออฟไลน์ของ IOWA ต้องใช้ Service Worker เป็นอย่างมาก เราพัฒนาไลบรารี 2 รายการเพื่อจัดการกรณีการใช้งานแบบออฟไลน์ 2 รูปแบบที่แตกต่างกัน โดยได้รับแรงบันดาลใจจากความต้องการในชีวิตจริงของ IOWA ไลบรารี sw-precache
ใช้เพื่อทำให้แคชทรัพยากรแบบคงที่ล่วงหน้าเป็นแบบอัตโนมัติ และไลบรารี sw-toolbox
ใช้เพื่อจัดการแคชรันไทม์และกลยุทธ์สำรอง
ไลบรารีเหล่านี้ทำงานร่วมกันได้อย่างลงตัว และช่วยให้เราใช้กลยุทธ์ที่มีประสิทธิภาพซึ่งแสดง "เชลล์" เนื้อหาแบบคงที่ของ IOWA จากแคชโดยตรงเสมอ และแสดงทรัพยากรแบบไดนามิกหรือจากระยะไกลจากเครือข่าย โดยมีการตอบกลับสำรองเป็นข้อมูลที่แคชไว้หรือแบบคงที่เมื่อจำเป็น
การแคชล่วงหน้าด้วย sw-precache
ทรัพยากรแบบคงที่ของ IOWA ไม่ว่าจะเป็น HTML, JavaScript, CSS และรูปภาพ ถือเป็นหัวใจสำคัญของเว็บแอปพลิเคชัน มีข้อกำหนดเฉพาะ 2 ประการที่สำคัญเมื่อพิจารณาแคชทรัพยากรเหล่านี้คือ เราต้องการตรวจสอบว่าทรัพยากรแบบคงที่ส่วนใหญ่ได้รับการแคชแล้ว และอัปเดตทรัพยากรดังกล่าวให้เป็นปัจจุบันเสมอ
sw-precache
สร้างขึ้นโดยคำนึงถึงข้อกำหนดเหล่านั้น
การผสานรวมเวลาที่บิลด์
sw-precache
ด้วยกระบวนการสร้างตาม gulp
ของ IOWA และเราใช้รูปแบบ glob หลายชุดเพื่อให้แน่ใจว่าได้สร้างรายการทรัพยากรแบบคงที่ทั้งหมดที่ IOWA ใช้อย่างครบถ้วน
staticFileGlobs: [
rootDir + '/bower_components/**/*.{html,js,css}',
rootDir + '/elements/**',
rootDir + '/fonts/**',
rootDir + '/images/**',
rootDir + '/scripts/**',
rootDir + '/styles/**/*.css',
rootDir + '/data-worker-scripts.js'
]
แนวทางอื่นๆ เช่น การเขียนโค้ดชื่อไฟล์ลงในอาร์เรย์อย่างถาวร และการอย่าลืมเพิ่มหมายเลขเวอร์ชันแคชทุกครั้งที่มีการเปลี่ยนแปลงไฟล์เหล่านี้ มีแนวโน้มที่จะเกิดข้อผิดพลาดได้สูงมาก โดยเฉพาะอย่างยิ่งเมื่อเรามีสมาชิกในทีมหลายคนที่ตรวจสอบโค้ด ไม่มีใครอยากทำให้การสนับสนุนแบบออฟไลน์ใช้งานไม่ได้ด้วยการละเว้นไฟล์ใหม่ในอาร์เรย์ที่ดูแลรักษาด้วยตนเอง การผสานรวมที่เวลาสร้างช่วยให้เราเปลี่ยนแปลงไฟล์ที่มีอยู่และเพิ่มไฟล์ใหม่ได้โดยไม่ต้องกังวล
การอัปเดตทรัพยากรที่แคชไว้
sw-precache
จะสร้างสคริปต์ Service Worker พื้นฐานที่มีแฮช MD5 ที่ไม่ซ้ำกันสําหรับแต่ละแหล่งข้อมูลที่แคชไว้ล่วงหน้า ทุกครั้งที่มีการเปลี่ยนแปลงทรัพยากรที่มีอยู่หรือมีการเพิ่มทรัพยากรใหม่ ระบบจะสร้างสคริปต์ Service Worker ขึ้นมาใหม่ ซึ่งจะทริกเกอร์ขั้นตอนการอัปเดต Service Worker โดยอัตโนมัติ ซึ่งระบบจะแคชทรัพยากรใหม่และล้างทรัพยากรที่ล้าสมัย
ทรัพยากรที่มีอยู่ซึ่งมีแฮช MD5 เหมือนกันจะถูกปล่อยไว้ตามเดิม ซึ่งหมายความว่าผู้ใช้ที่เคยเข้าชมเว็บไซต์ก่อนที่จะดาวน์โหลดชุดทรัพยากรที่มีการเปลี่ยนแปลงน้อยที่สุด ทำให้ได้รับประสบการณ์การใช้งานที่มีประสิทธิภาพมากกว่าการหมดอายุของแคชทั้งหมดจำนวนมาก
ระบบจะดาวน์โหลดและแคชไฟล์แต่ละไฟล์ที่ตรงกับรูปแบบ Glob รายการใดรายการหนึ่งเมื่อผู้ใช้เข้าชม IOWA เป็นครั้งแรก เราพยายามอย่างเต็มที่เพื่อให้แน่ใจว่ามีการแคชเฉพาะทรัพยากรที่สำคัญซึ่งจําเป็นสําหรับการแสดงผลหน้าเว็บไว้ล่วงหน้า เนื้อหารอง เช่น สื่อที่ใช้ในการทดสอบภาพและเสียง หรือรูปโปรไฟล์ของผู้บรรยายในเซสชัน ไม่ได้มีการแคชไว้ล่วงหน้า แต่เราใช้คลัง sw-toolbox
เพื่อจัดการคำขอทรัพยากรเหล่านั้นแบบออฟไลน์แทน
sw-toolbox
สำหรับความต้องการแบบไดนามิกทั้งหมดของเรา
ดังที่ได้กล่าวไปแล้ว การแคชทรัพยากรทั้งหมดที่เว็บไซต์จําเป็นต้องใช้เมื่อออฟไลน์นั้นไม่สามารถทำได้ ทรัพยากรบางอย่างมีขนาดใหญ่เกินไปหรือมีการใช้งานไม่บ่อยนักจึงไม่ค่อยคุ้มค่า และทรัพยากรอื่นๆ เป็นแบบไดนามิก เช่น การตอบกลับจาก API หรือบริการระยะไกล แต่การที่คำขอไม่ได้แคชไว้ล่วงหน้าไม่ได้หมายความว่าจะต้องส่งผลให้เกิด NetworkError
sw-toolbox
ให้ความยืดหยุ่นในการใช้งานตัวแฮนเดิลคำขอที่จัดการแคชรันไทม์สำหรับทรัพยากรบางรายการและคำขอสำรองที่กำหนดเองสำหรับทรัพยากรอื่นๆ นอกจากนี้เรายังใช้เพื่ออัปเดตทรัพยากรที่แคชไว้ก่อนหน้านี้เพื่อตอบสนองต่อข้อความ Push ด้วย
ต่อไปนี้คือตัวอย่างตัวจัดการคําขอที่กําหนดเองซึ่งเราสร้างขึ้นจาก sw-toolbox การผสานรวมกับสคริปต์ Service Worker พื้นฐานนั้นทำได้ง่ายมากผ่าน importScripts parameter
ของ sw-precache
ซึ่งจะดึงไฟล์ JavaScript แบบสแตนด์อโลนเข้ามาไว้ในขอบเขตของ Service Worker
การทดสอบเสียง/ภาพ
สำหรับการทดสอบเสียง/ภาพ เราใช้กลยุทธ์แคช networkFirst
ของ sw-toolbox
ระบบจะส่งคำขอ HTTP ทั้งหมดที่ตรงกับรูปแบบ URL สำหรับการทดสอบกับเครือข่ายก่อน และหากมีการส่งคืนการตอบสนองที่สำเร็จ การตอบสนองดังกล่าวจะถูกซ่อนโดยใช้ Cache Storage API
หากมีคำขอตามมาเมื่อเครือข่ายไม่พร้อมใช้งาน ระบบจะใช้คำตอบที่แคชไว้ก่อนหน้านี้
เนื่องจากแคชได้รับการอัปเดตโดยอัตโนมัติทุกครั้งที่เครือข่ายตอบกลับสําเร็จ เราจึงไม่ต้องกำหนดเวอร์ชันทรัพยากรหรือกำหนดให้รายการหมดอายุโดยเฉพาะ
toolbox.router.get('/experiment/(.+)', toolbox.networkFirst);
รูปโปรไฟล์ผู้บรรยาย
สำหรับรูปโปรไฟล์ของผู้บรรยาย เป้าหมายของเราคือแสดงรูปภาพของผู้บรรยายที่แคชไว้ก่อนหน้านี้หากมี หากไม่มี เราจะใช้เครือข่ายเพื่อดึงข้อมูลรูปภาพ หากคำขอเครือข่ายนั้นไม่สำเร็จ เราจะใช้รูปภาพตัวยึดตำแหน่งทั่วไปที่แคชไว้ล่วงหน้า (และจะพร้อมใช้งานเสมอ) เป็นทางเลือกสุดท้าย นี่เป็นกลยุทธ์ที่ใช้กันโดยทั่วไปเมื่อต้องจัดการกับรูปภาพที่แทนที่ได้ด้วยตัวยึดตําแหน่งทั่วไป และติดตั้งใช้งานได้ง่ายด้วยการต่อเชนตัวแฮนเดิล cacheFirst
และ cacheOnly
ของ sw-toolbox
var DEFAULT_PROFILE_IMAGE = 'images/touch/homescreen96.png';
function profileImageRequest(request) {
return toolbox.cacheFirst(request).catch(function() {
return toolbox.cacheOnly(new Request(DEFAULT_PROFILE_IMAGE));
});
}
toolbox.precache([DEFAULT_PROFILE_IMAGE]);
toolbox.router.get('/(.+)/images/speakers/(.*)',
profileImageRequest,
{origin: /.*\.googleapis\.com/});
การอัปเดตกำหนดการของผู้ใช้
ฟีเจอร์หลักอย่างหนึ่งของ IOWA คืออนุญาตให้ผู้ใช้ที่ลงชื่อเข้าใช้สร้างและดูแลรักษากำหนดเวลาของเซสชันที่วางแผนจะเข้าร่วม ตามที่คาดไว้ การอัปเดตเซสชันเกิดขึ้นผ่านคําขอ HTTP POST
ไปยังเซิร์ฟเวอร์แบ็กเอนด์ และเราได้ใช้เวลาสักพักในการหาวิธีที่ดีที่สุดในการจัดการคําขอที่แก้ไขสถานะเหล่านั้นเมื่อผู้ใช้ออฟไลน์ เราใช้การผสมผสานระหว่าง IndexedDB ที่จัดคิวคำขอที่ดำเนินการไม่สำเร็จไว้ ร่วมกับตรรกะในหน้าเว็บหลักที่ตรวจสอบ IndexedDB สำหรับคำขอที่อยู่ในคิวและลองอีกครั้งกับคำขอที่พบ
var DB_NAME = 'shed-offline-session-updates';
function queueFailedSessionUpdateRequest(request) {
simpleDB.open(DB_NAME).then(function(db) {
db.set(request.url, request.method);
});
}
function handleSessionUpdateRequest(request) {
return global.fetch(request).then(function(response) {
if (response.status >= 500) {
return Response.error();
}
return response;
}).catch(function() {
queueFailedSessionUpdateRequest(request);
});
}
toolbox.router.put('/(.+)api/v1/user/schedule/(.+)',
handleSessionUpdateRequest);
toolbox.router.delete('/(.+)api/v1/user/schedule/(.+)',
handleSessionUpdateRequest);
เนื่องจากมีการลองอีกครั้งจากบริบทของหน้าหลัก เราจึงมั่นใจได้ว่าการลองดังกล่าวมีชุดข้อมูลเข้าสู่ระบบของผู้ใช้ใหม่ เมื่อการลองอีกครั้งสำเร็จแล้ว เราจะแสดงข้อความเพื่อแจ้งให้ผู้ใช้ทราบว่าระบบได้อัปเดตที่รอดำเนินการก่อนหน้านี้แล้ว
simpleDB.open(QUEUED_SESSION_UPDATES_DB_NAME).then(function(db) {
var replayPromises = [];
return db.forEach(function(url, method) {
var promise = IOWA.Request.xhrPromise(method, url, true).then(function() {
return db.delete(url).then(function() {
return true;
});
});
replayPromises.push(promise);
}).then(function() {
if (replayPromises.length) {
return Promise.all(replayPromises).then(function() {
IOWA.Elements.Toast.showMessage(
'My Schedule was updated with offline changes.');
});
}
});
}).catch(function() {
IOWA.Elements.Toast.showMessage(
'Offline changes could not be applied to My Schedule.');
});
Google Analytics แบบออฟไลน์
ในทํานองเดียวกัน เราได้ติดตั้งใช้งานตัวแฮนเดิลเพื่อจัดคิวคําขอ Google Analytics ที่ดำเนินการไม่สำเร็จและพยายามเล่นซ้ำในภายหลังเมื่อเครือข่ายพร้อมใช้งาน แนวทางนี้ช่วยให้คุณได้รับข้อมูลเชิงลึกจาก Google Analytics แม้จะออฟไลน์อยู่ เราได้เพิ่มพารามิเตอร์ qt
ลงในคําขอที่รอดำเนินการแต่ละรายการ โดยตั้งค่าเป็นระยะเวลาที่ผ่านไปตั้งแต่มีการพยายามส่งคําขอครั้งแรก เพื่อให้แน่ใจว่าเวลาระบุแหล่งที่มาของเหตุการณ์ที่เหมาะสมจะส่งไปยังแบ็กเอนด์ของ Google Analytics Google Analytics รองรับค่าอย่างเป็นทางการสำหรับ qt
สูงสุดไม่เกิน 4 ชั่วโมง เราจึงพยายามอย่างดีที่สุดที่จะเล่นคำขอเหล่านั้นซ้ำโดยเร็วที่สุดทุกครั้งที่โปรแกรมทำงานของบริการเริ่มต้นทำงาน
var DB_NAME = 'offline-analytics';
var EXPIRATION_TIME_DELTA = 86400000;
var ORIGIN = /https?:\/\/((www|ssl)\.)?google-analytics\.com/;
function replayQueuedAnalyticsRequests() {
simpleDB.open(DB_NAME).then(function(db) {
db.forEach(function(url, originalTimestamp) {
var timeDelta = Date.now() - originalTimestamp;
var replayUrl = url + '&qt=' + timeDelta;
fetch(replayUrl).then(function(response) {
if (response.status >= 500) {
return Response.error();
}
db.delete(url);
}).catch(function(error) {
if (timeDelta > EXPIRATION_TIME_DELTA) {
db.delete(url);
}
});
});
});
}
function queueFailedAnalyticsRequest(request) {
simpleDB.open(DB_NAME).then(function(db) {
db.set(request.url, Date.now());
});
}
function handleAnalyticsCollectionRequest(request) {
return global.fetch(request).then(function(response) {
if (response.status >= 500) {
return Response.error();
}
return response;
}).catch(function() {
queueFailedAnalyticsRequest(request);
});
}
toolbox.router.get('/collect',
handleAnalyticsCollectionRequest,
{origin: ORIGIN});
toolbox.router.get('/analytics.js',
toolbox.networkFirst,
{origin: ORIGIN});
replayQueuedAnalyticsRequests();
หน้า Landing Page ของข้อความ Push
Service Worker ไม่เพียงจัดการฟังก์ชันการทำงานแบบออฟไลน์ของ IOWA เท่านั้น แต่ยังขับเคลื่อนข้อความ Push ที่เราใช้แจ้งผู้ใช้เกี่ยวกับการอัปเดตเซสชันที่บุ๊กมาร์กไว้ หน้า Landing Page ที่เชื่อมโยงกับการแจ้งเตือนเหล่านั้นจะแสดงรายละเอียดเซสชันที่อัปเดต หน้า Landing Page เหล่านั้นได้รับการแคชไว้แล้วโดยเป็นส่วนหนึ่งของเว็บไซต์โดยรวม ดังนั้นหน้าดังกล่าวจึงทํางานแบบออฟไลน์ได้อยู่แล้ว แต่เราต้องตรวจสอบว่ารายละเอียดเซสชันในหน้านั้นทันสมัยอยู่เสมอ แม้ว่าจะดูแบบออฟไลน์ก็ตาม โดยเราได้แก้ไขข้อมูลเมตาเซสชันที่แคชไว้ก่อนหน้านี้ด้วยข้อมูลอัปเดตที่ทริกเกอร์ข้อความ Push และจัดเก็บผลลัพธ์ไว้ในแคช ระบบจะใช้ข้อมูลล่าสุดนี้ในครั้งถัดไปที่เปิดหน้ารายละเอียดเซสชัน ไม่ว่าจะเปิดทางออนไลน์หรือออฟไลน์
caches.open(toolbox.options.cacheName).then(function(cache) {
cache.match('api/v1/schedule').then(function(response) {
if (response) {
parseResponseJSON(response).then(function(schedule) {
sessions.forEach(function(session) {
schedule.sessions[session.id] = session;
});
cache.put('api/v1/schedule',
new Response(JSON.stringify(schedule)));
});
} else {
toolbox.cache('api/v1/schedule');
}
});
});
ข้อมูลที่ควรพิจารณาและข้อควรพิจารณา
แน่นอนว่าไม่มีใครทำงานในโปรเจ็กต์ขนาดของ IOWA ได้โดยไม่พบปัญหา ต่อไปนี้คือปัญหาบางส่วนที่เราพบและวิธีแก้ปัญหา
เนื้อหาไม่มีอัปเดต
เมื่อวางแผนกลยุทธ์การแคช ไม่ว่าจะเป็นการใช้งานผ่าน Service Worker หรือแคชเบราว์เซอร์มาตรฐาน คุณต้องเลือกระหว่างการนําส่งทรัพยากรให้เร็วที่สุดกับการนำส่งทรัพยากรที่ใหม่ที่สุด ผ่าน sw-precache
เราใช้กลยุทธ์ "ใช้แคชก่อน" แบบเชิงรุกสำหรับเชลล์ของแอปพลิเคชัน ซึ่งหมายความว่า Service Worker จะไม่ตรวจสอบเครือข่ายเพื่อหาการอัปเดตก่อนที่จะแสดง HTML, JavaScript และ CSS ในหน้าเว็บ
แต่โชคดีที่เราสามารถใช้ประโยชน์จากเหตุการณ์วงจรชีวิตของ Service Worker เพื่อตรวจหาว่าเนื้อหาใหม่พร้อมใช้งานเมื่อใดหลังจากที่โหลดหน้าเว็บแล้ว เมื่อตรวจพบ Service Worker ที่อัปเดต เราจะแสดงข้อความโทสต์แก่ผู้ใช้เพื่อแจ้งให้ทราบว่าควรโหลดหน้าเว็บซ้ำเพื่อดูเนื้อหาใหม่ล่าสุด
if (navigator.serviceWorker && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.onstatechange = function(e) {
if (e.target.state === 'redundant') {
var tapHandler = function() {
window.location.reload();
};
IOWA.Elements.Toast.showMessage(
'Tap here or refresh the page for the latest content.',
tapHandler);
}
};
}
ตรวจสอบว่าเนื้อหาคงที่เป็นแบบคงที่
sw-precache
ใช้แฮช MD5 ของเนื้อหาไฟล์ในเครื่อง และจะดึงข้อมูลเฉพาะแหล่งข้อมูลที่แฮชมีการเปลี่ยนแปลง ซึ่งหมายความว่าทรัพยากรจะพร้อมใช้งานในหน้าเว็บเกือบจะทันที แต่ก็หมายความว่าเมื่อแคชข้อมูลบางอย่างแล้ว ระบบจะแคชข้อมูลนั้นไว้จนกว่าจะกำหนดแฮชใหม่ในสคริปต์ Service Worker ที่อัปเดตแล้ว
เราพบปัญหากับลักษณะการทำงานนี้ในระหว่าง I/O เนื่องจากแบ็กเอนด์ของเราต้องอัปเดตรหัสวิดีโอ YouTube ของสตรีมแบบสดแบบไดนามิกสำหรับแต่ละวันของการประชุม เนื่องจากไฟล์เทมเพลตพื้นฐานเป็นแบบคงที่และไม่มีการเปลี่ยนแปลง ระบบจึงไม่ทริกเกอร์ขั้นตอนการอัปเดต Service Worker และสิ่งที่ควรจะเป็นคำตอบแบบไดนามิกจากเซิร์ฟเวอร์ที่มีวิดีโอ YouTube ที่อัปเดตใหม่กลับกลายเป็นคำตอบที่แคชไว้สำหรับผู้ใช้จํานวนหนึ่ง
คุณหลีกเลี่ยงปัญหาประเภทนี้ได้โดยตรวจสอบว่าเว็บแอปพลิเคชันมีโครงสร้างเพื่อให้เชลล์เป็นแบบคงที่เสมอและสามารถแคชล่วงหน้าได้อย่างปลอดภัย ขณะที่โหลดทรัพยากรแบบไดนามิกที่แก้ไขเชลล์แยกต่างหาก
หลีกเลี่ยงการแคชคำขอการแคชล่วงหน้า
เมื่อ sw-precache
ส่งคำขอทรัพยากรเพื่อแคชไว้ล่วงหน้า ระบบจะใช้คำตอบเหล่านั้นอย่างไม่มีกำหนดตราบใดที่คิดว่าแฮช MD5 ของไฟล์นั้นไม่เปลี่ยนแปลง ซึ่งหมายความว่าการตรวจสอบว่าการตอบกลับคำขอการแคชล่วงหน้าเป็นคำตอบใหม่ล่าสุดและไม่ได้มาจากแคช HTTP ของเบราว์เซอร์นั้นสำคัญอย่างยิ่ง (ใช่ คำขอ fetch()
ที่สร้างขึ้นใน Service Worker สามารถตอบกลับด้วยข้อมูลจากแคช HTTP ของเบราว์เซอร์ได้)
sw-precache
จะเพิ่มพารามิเตอร์การค้นหาที่ป้องกันการแคชต่อท้าย URL แต่ละรายการที่ขอโดยอัตโนมัติ เพื่อให้แน่ใจว่าการตอบกลับที่เราแคชไว้ล่วงหน้านั้นมาจากเครือข่ายโดยตรง ไม่ใช่แคช HTTP ของเบราว์เซอร์ หากคุณไม่ได้ใช้ sw-precache
และกำลังใช้กลยุทธ์การตอบสนองแบบใช้แคชเป็นอันดับแรก โปรดทําสิ่งที่คล้ายกันในโค้ดของคุณเอง
โซลูชันที่สะอาดกว่าในการหลีกเลี่ยงแคชคือการตั้งค่าโหมดแคชของ Request
แต่ละรายการที่ใช้สำหรับการแคชล่วงหน้าเป็น reload
ซึ่งจะช่วยให้มั่นใจได้ว่าการตอบกลับมาจากเครือข่าย อย่างไรก็ตาม ขณะเขียนบทความนี้ Chrome ยังไม่รองรับตัวเลือกโหมดแคช
การรองรับการเข้าสู่ระบบและออกจากระบบ
IOWA อนุญาตให้ผู้ใช้เข้าสู่ระบบโดยใช้บัญชี Google และอัปเดตกำหนดเวลากิจกรรมที่กําหนดเอง แต่นั่นก็หมายความว่าผู้ใช้อาจออกจากระบบในภายหลังด้วย การแคชข้อมูลคำตอบที่ปรับตามโปรไฟล์ของผู้ใช้เป็นหัวข้อที่เข้าใจยาก และไม่มีแนวทางที่ถูกต้องเพียงวิธีเดียวเสมอไป
เนื่องจากการดูกำหนดเวลาส่วนตัวของคุณแม้ในขณะที่ออฟไลน์เป็นหัวใจสําคัญของประสบการณ์การใช้งาน IOWA เราจึงตัดสินใจว่าการใช้ข้อมูลที่แคชไว้นั้นเหมาะสม เมื่อผู้ใช้ออกจากระบบแล้ว เราล้างข้อมูลเซสชันที่แคชไว้ก่อนหน้านี้
self.addEventListener('message', function(event) {
if (event.data === 'clear-cached-user-data') {
caches.open(toolbox.options.cacheName).then(function(cache) {
cache.keys().then(function(requests) {
return requests.filter(function(request) {
return request.url.indexOf('api/v1/user/') !== -1;
});
}).then(function(userDataRequests) {
userDataRequests.forEach(function(userDataRequest) {
cache.delete(userDataRequest);
});
});
});
}
});
ระวังพารามิเตอร์การค้นหาเพิ่มเติม
เมื่อ Service Worker ตรวจสอบการตอบกลับที่แคชไว้ ก็จะใช้ URL คำขอเป็นคีย์ โดยค่าเริ่มต้น URL คำขอต้องตรงกับ URL ที่ใช้จัดเก็บคำตอบที่แคชไว้ทุกประการ ซึ่งรวมถึงพารามิเตอร์การค้นหาในส่วน search ของ URL
เรื่องนี้ก็ทำให้ปัญหาเกิดขึ้นระหว่างการพัฒนา เมื่อเราเริ่มใช้พารามิเตอร์ของ URL เพื่อติดตามว่าการเข้าชมมาจากที่ใด ตัวอย่างเช่น เราได้เพิ่มพารามิเตอร์ utm_source=notification
ลงใน URL ที่เปิดเมื่อคลิกการแจ้งเตือนรายการใดรายการหนึ่งของเรา และใช้ utm_source=web_app_manifest
ใน start_url
สำหรับไฟล์ Manifest ของเว็บแอป
URL ที่ก่อนหน้านี้ตรงกับคำตอบที่แคชไว้จะแสดงเป็น "ไม่พบ" เมื่อเพิ่มพารามิเตอร์เหล่านั้นต่อท้าย
ปัญหานี้ได้รับการแก้ไขบางส่วนด้วยตัวเลือก ignoreSearch
ซึ่งสามารถใช้ได้เมื่อเรียกใช้ Cache.match()
ขออภัย Chrome ยังไม่ได้
สนับสนุน ignoreSearch
และถึงแม้จะรองรับ ก็เป็นเช่นนั้น สิ่งที่เราต้องการคือวิธีไม่ต้องสนใจพารามิเตอร์การค้นหาของ URL บางรายการ และอย่าลืมนำพารามิเตอร์อื่นๆ ที่มีความหมายมาพิจารณาด้วย
เราจึงขยาย sw-precache
เพื่อตัดพารามิเตอร์การค้นหาบางรายการออกก่อนที่จะตรวจสอบการจับคู่แคช และอนุญาตให้นักพัฒนาแอปปรับแต่งพารามิเตอร์ที่จะละเว้นผ่านตัวเลือก ignoreUrlParametersMatching
การใช้งานพื้นฐานมีดังนี้
function stripIgnoredUrlParameters(originalUrl, ignoredRegexes) {
var url = new URL(originalUrl);
url.search = url.search.slice(1)
.split('&')
.map(function(kv) {
return kv.split('=');
})
.filter(function(kv) {
return ignoredRegexes.every(function(ignoredRegex) {
return !ignoredRegex.test(kv[0]);
});
})
.map(function(kv) {
return kv.join('=');
})
.join('&');
return url.toString();
}
ผลกระทบที่มีต่อคุณ
การผสานรวม Service Worker ในเว็บแอป Google I/O น่าจะเป็นการใช้งานในชีวิตจริงที่ซับซ้อนที่สุดเท่าที่เคยมีมาจนถึงตอนนี้ เราหวังเป็นอย่างยิ่งว่าชุมชนนักพัฒนาเว็บจะใช้เครื่องมือที่เราสร้างขึ้น sw-precache
และ sw-toolbox
รวมถึงเทคนิคที่เราอธิบายเพื่อเพิ่มศักยภาพให้กับเว็บแอปพลิเคชันของคุณ
Service Worker เป็นการเพิ่มประสิทธิภาพแบบเป็นขั้นเป็นตอนที่คุณเริ่มใช้ได้ตั้งแต่วันนี้ และเมื่อใช้เป็นส่วนหนึ่งของเว็บแอปที่มีโครงสร้างเหมาะสม ผู้ใช้จะได้รับประโยชน์ด้านความเร็วและการใช้งานแบบออฟไลน์อย่างมาก