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

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

สรุป

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