Service Worker ในเวอร์ชันที่ใช้งานจริง

เจฟ พอสนิก
เจฟฟ์ พอสนิก

ภาพหน้าจอแนวตั้ง

สรุป

เรียนรู้วิธีที่เราใช้ไลบรารี 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 รวมถึงเทคนิคที่เราอธิบายเพื่อเพิ่มประสิทธิภาพเว็บแอปพลิเคชันของคุณเอง โปรแกรมทำงานของบริการเป็นการเพิ่มประสิทธิภาพแบบก้าวหน้าที่คุณเริ่มใช้งานได้ตั้งแต่วันนี้ และเมื่อใช้เป็นส่วนหนึ่งของเว็บแอปที่มีโครงสร้างที่เหมาะสม ความเร็วและประโยชน์แบบออฟไลน์จะเป็นประโยชน์ต่อผู้ใช้อย่างยิ่ง