สรุป
เรียนรู้วิธีที่เราใช้ไลบรารี Service Worker เพื่อทำให้เว็บแอป Google I/O 2015 ทำงานรวดเร็วและเน้นออฟไลน์เป็นหลัก
ภาพรวม
เว็บแอป Google I/O 2015 ในปีนี้เขียนโดยทีมนักพัฒนาแอปสัมพันธ์ของ Google โดยใช้การออกแบบโดยเพื่อนของเราที่ Instrument ที่เขียนการทดสอบเสียง/ภาพชั้นยอด ภารกิจที่ทีมของเรายึดมั่นคือการทำให้เว็บแอป I/O (ซึ่งเราจะใช้ชื่อว่า IOWA) แสดงทุกสิ่งที่เว็บสมัยใหม่ทำได้ ประสบการณ์เต็มรูปแบบที่เน้นออฟไลน์เป็นหลักอยู่ในลำดับต้นๆ ของรายการฟีเจอร์ที่ขาดไม่ได้
หากได้อ่านบทความอื่นๆ ในเว็บไซต์นี้เมื่อเร็วๆ นี้ คุณพบกับโปรแกรมทำงานของบริการอย่างไม่น่าเชื่อ และจะไม่แปลกใจเลยที่จะได้ทราบว่าการสนับสนุนแบบออฟไลน์ของ IOWA นั้นพึ่งพาเป็นอย่างยิ่ง ด้วยแรงผลักดันจากความต้องการที่แท้จริงของ IOWA เราจึงได้พัฒนาห้องสมุด 2 แห่งเพื่อรองรับกรณีการใช้งานออฟไลน์ 2 แบบ ได้แก่ sw-precache
เพื่อกำหนดทรัพยากรคงที่ล่วงหน้าโดยอัตโนมัติ และ sw-toolbox
เพื่อจัดการการแคชรันไทม์และกลยุทธ์สำรอง
ไลบรารีเหล่านี้ส่งเสริมกันและกันได้ดีและช่วยให้เรานำกลยุทธ์ที่มีประสิทธิภาพไปใช้ โดยจะแสดง " Shell" เนื้อหาแบบคงที่ของ IOWA โดยตรงจากแคชเสมอ และทรัพยากรแบบไดนามิกหรือระยะไกลจะแสดงจากเครือข่าย โดยมีการสำรองไปยังการตอบกลับที่แคชไว้หรือคงที่เมื่อจำเป็น
การแคชล่วงหน้าด้วย sw-precache
ทรัพยากรแบบคงที่ของ IOWA ซึ่งได้แก่ HTML, JavaScript, CSS และรูปภาพ ถือเป็น Shell หลักสำหรับเว็บแอปพลิเคชัน มีข้อกำหนดเฉพาะ 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 โดยอัตโนมัติ ซึ่งจะมีการแคชทรัพยากรใหม่และนำทรัพยากรที่ล้าสมัยออกอย่างถาวร
ทรัพยากรที่มีอยู่ซึ่งมีแฮช MD5 เหมือนกันจะคงไว้ตามเดิม ซึ่งหมายความว่าผู้ใช้ที่เข้าชมเว็บไซต์ก่อนที่จะมาดาวน์โหลดชุดทรัพยากรที่มีการเปลี่ยนแปลงจำนวนน้อยนิด ซึ่งทำให้เกิดประสบการณ์ที่มีประสิทธิภาพมากกว่าในกรณีที่แคชทั้งหมดหมดอายุในวงกว้าง
แต่ละไฟล์ที่ตรงกับรูปแบบ glob ใดรูปแบบหนึ่งจะได้รับการดาวน์โหลดและแคชในครั้งแรกที่ผู้ใช้เข้าชม IOWA เราพยายามดูแลให้มีการแคชล่วงหน้าเฉพาะทรัพยากรที่สำคัญซึ่งจำเป็นต้องใช้ในการแสดงผลหน้าเว็บ เนื้อหารอง เช่น สื่อที่ใช้ในการทดสอบเสียง/ภาพหรือรูปโปรไฟล์ของผู้บรรยายของเซสชันไม่ได้ตั้งใจไม่ได้เก็บแคชล่วงหน้าไว้ แต่เราใช้ไลบรารี sw-toolbox
เพื่อจัดการคำขอออฟไลน์สำหรับทรัพยากรเหล่านั้นแทน
sw-toolbox
เพื่อตอบโจทย์ความต้องการเฉพาะตัวของเรา
ดังที่กล่าวไปแล้ว การแคชทรัพยากรทุกรายการที่เว็บไซต์ต้องใช้เพื่อทำงานแบบออฟไลน์ล่วงหน้าเป็นไปไม่ได้ ทรัพยากรบางรายการมีขนาดใหญ่เกินไปหรือใช้งานไม่บ่อยนักเพื่อให้คุ้มค่า และทรัพยากรอื่นๆ เป็นแบบไดนามิก เช่น การตอบกลับจาก API หรือบริการระยะไกล แต่คำขอไม่ได้ถูกแคชล่วงหน้าไม่ได้หมายความว่าคำขอจะไม่ส่งผลให้มี NetworkError
sw-toolbox
ทำให้เรามีความยืดหยุ่นในการใช้เครื่องจัดการคำขอที่จัดการแคชรันไทม์สำหรับทรัพยากรบางรายการและข้อมูลสำรองที่กำหนดเองสำหรับรายการอื่นๆ นอกจากนี้ เรายังใช้รายงานนี้เพื่ออัปเดตทรัพยากรที่แคชไว้ก่อนหน้านี้เพื่อตอบสนองต่อข้อความ Push
ต่อไปนี้เป็นตัวอย่างตัวแฮนเดิลคำขอที่กำหนดเองที่เราสร้างขึ้นเพิ่มเติมจากกล่องเครื่องมือ sw ผสานรวมไฟล์เหล่านั้นกับสคริปต์ 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 ชั่วโมง เราจึงพยายามอย่างสุดความสามารถที่จะเล่นคำขอเหล่านั้นซ้ำโดยเร็วที่สุดเท่าที่จะเป็นไปได้ในแต่ละครั้งที่ Service Worker เริ่มทำงาน
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
โปรแกรมทำงานของบริการไม่เพียงแค่ต้องจัดการฟังก์ชันออฟไลน์ของ 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 โดยไม่ได้เจอกับปัญหาเล็กๆ น้อยๆ ต่อไปนี้คือปัญหาบางส่วนที่เราเจอและวิธีที่เราใช้แก้ไขปัญหา
เนื้อหาไม่มีอัปเดต
เมื่อใดก็ตามที่คุณวางแผนกลยุทธ์การแคช ไม่ว่าจะใช้งานผ่านโปรแกรมทำงานของบริการหรือใช้ร่วมกับแคชของเบราว์เซอร์มาตรฐาน จะมีข้อดีข้อเสียคือการนำส่งทรัพยากรโดยเร็วที่สุดกับการนำส่งทรัพยากรใหม่ล่าสุด ผ่านทาง sw-precache
เราได้นำกลยุทธ์ที่เน้นแคชเป็นหลักสำหรับแอปพลิเคชันของเรามาใช้ในเชิงรุก ซึ่งหมายความว่าโปรแกรมทำงานของบริการจะไม่ตรวจสอบเครือข่ายเพื่อหาการอัปเดตก่อนที่จะแสดงผล 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 ของสตรีมแบบสดแบบไดนามิกสำหรับแต่ละวันที่มีการประชุม เนื่องจากไฟล์เทมเพลตที่เกี่ยวข้องนั้นไม่เปลี่ยนแปลงและไม่มีการเปลี่ยนแปลง ขั้นตอนการอัปเดตโปรแกรมทำงานของบริการจึงไม่มีการเรียกใช้ ขั้นตอนอะไรที่เป็นการตอบสนองแบบไดนามิกจากเซิร์ฟเวอร์ที่มีการอัปเดตวิดีโอ YouTube จึงเป็นคำตอบที่แคชไว้สำหรับผู้ใช้จำนวนหนึ่ง
คุณหลีกเลี่ยงปัญหาประเภทนี้ได้ด้วยการตรวจสอบว่าเว็บแอปพลิเคชันของคุณมีโครงสร้างเพื่อให้ Shell เป็นแบบคงที่เสมอและแคชล่วงหน้าได้อย่างปลอดภัย ในขณะที่ทรัพยากรแบบไดนามิกที่แก้ไข Shell จะโหลดขึ้นอย่างอิสระ
ป้องกันแคชคำขอแคชล่วงหน้า
เมื่อ sw-precache
ส่งคำขอทรัพยากรเพื่อแคชล่วงหน้า ระบบจะใช้การตอบสนองเหล่านั้นไปเรื่อยๆ ตราบใดที่คิดว่าแฮช MD5 สำหรับไฟล์ไม่เปลี่ยนแปลง ซึ่งหมายความว่าสิ่งที่สำคัญอย่างยิ่งคือต้องตรวจสอบว่าการตอบกลับคำขอแคชล่วงหน้าเป็นข้อความใหม่ และไม่ได้แสดงผลจากแคช HTTP ของเบราว์เซอร์ (ใช่ คำขอ fetch()
รายการที่สร้างขึ้นในโปรแกรมทำงานของบริการจะตอบกลับด้วยข้อมูลจากแคช 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);
});
});
});
}
});
โปรดระวังพารามิเตอร์การค้นหาเพิ่มเติม
เมื่อโปรแกรมทำงานของบริการตรวจสอบการตอบกลับที่แคชไว้ โปรแกรมจะใช้ 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
รวมถึงเทคนิคที่เราอธิบายเพื่อเพิ่มประสิทธิภาพเว็บแอปพลิเคชันของคุณเอง
โปรแกรมทำงานของบริการเป็นการเพิ่มประสิทธิภาพแบบก้าวหน้าที่คุณเริ่มใช้งานได้ตั้งแต่วันนี้ และเมื่อใช้เป็นส่วนหนึ่งของเว็บแอปที่มีโครงสร้างที่เหมาะสม ความเร็วและประโยชน์แบบออฟไลน์จะเป็นประโยชน์ต่อผู้ใช้อย่างยิ่ง