- From: Jake Archibald <notifications@github.com>
- Date: Fri, 23 Apr 2021 08:11:17 -0700
- To: w3c/ServiceWorker <ServiceWorker@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <w3c/ServiceWorker/issues/1585@github.com>
We blocked `import()` in service workers because we weren't sure how they should work, but it wasn't intended to be a forever-fix. Now that modules are becoming more popular, and browsers now support module service workers, how should `import()` work? Some thoughts: - It should work the same in classic service workers as it does with module service workers. - Ideally it should promote patterns that work offline. - Loading of a module in one service worker cannot be impacted by another service worker. Some ideas: --- ## Option 1 Before the service worker is "installed", calls to `import()` are fetched, bypassing all service workers. These module resources are cached along with the service worker script. After the service worker is installed, calls to `import()` will only use resources that are cached along with the service worker script, otherwise they will fail. Eg: ```js addEventListener('install', (event) => { event.waitUntil((async () => { const cache = await caches.open('static-v1'); return Promise.all([ cache.addAll(urls), import('./big-script.js'), import('./another-big-script.js'), ]); })()); }); addEventListener('fetch', (event) => { event.respondWith((async () => { if (whatever) { const { complicatedThing } = await import('./big-script.js'); // …etc… } })()); }); ``` - ✅ Compatible with classic and module service workers - ✅ Promotes working offline - ✅ Similar to how `importScripts` works - ✅ Don't need to know the full import tree, except for dynamic imports - ⚠️ Requires parsing (and maybe executing) stuff in the install phase that isn't actually used Another idea: --- ## Option 2 Before the service worker is "activated", calls to `import()` just go to the network, bypassing all service workers. After the service worker is "activated", calls to `import()` will go via that service worker's `"fetch"` event. ```js addEventListener('install', (event) => { event.waitUntil((async () => { const cache = await caches.open('static-v1'); return cache.addAll([ ...urls, './big-script.js', './big-script-dependency.js', './another-big-script.js', './another-big-script-dependency.js', ]); })()); }); addEventListener('fetch', (event) => { event.respondWith((async () => { if (whatever) { const { complicatedThing } = await import('./big-script.js'); } return caches.match(event.request); })()); }); ``` - ✅ Compatible with classic and module service workers - ✅ Scripts can be cached without parsing them - ⚠️ Need to list out all the module's dependencies - ⚠️ It can work offline, via the same mechanism as pages, but it can also become network dependent. It's versatile, but folks might not test for it? - ⚠️ Implementation complexities? I think it's the first time a request from a service worker will go through its own fetch event. We could overcome the dependencies issue with something like `cache.addModule('./big-script.js')`, which would crawl the tree similar to `modulepreload`. That means we're having to parse the modules, but it doesn't need to execute them. --- I preferred "option 2" when it was just in my head, but now I've written it down and thought it through, I don't think I like it. So, what about: ## Option 1.5 Before the service worker is "installed", calls to `import()` fail. Before the service worker is "installed", calls to `modulePreload(url)` crawl a module tree similar to `modulepreload`, and cache the script along with the service worker script. This returns a promise that indicates success. After the service worker is "installed", calls to `import()` will only use resources that are cached along with the service worker script, otherwise they will fail. After the service worker is "installed", calls to `modulePreload(url)` reject. Eg: ```js addEventListener('install', (event) => { event.waitUntil((async () => { const cache = await caches.open('static-v1'); return Promise.all([ cache.addAll(urls), modulePreload('./big-script.js'), modulePreload('./another-big-script.js'), ]); })()); }); addEventListener('fetch', (event) => { event.respondWith((async () => { if (whatever) { const { complicatedThing } = await import('./big-script.js'); // …etc… } })()); }); ``` - ✅ Compatible with classic and module service workers - ✅ Promotes working offline - ✅ Don't need to know the full import tree, except for dynamic imports - ✅ Requires parsing the scripts at install time, but avoids executing them Thoughts @wanderview @asutherland @jeffposnick @mfalken @youennf? -- You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/w3c/ServiceWorker/issues/1585
Received on Friday, 23 April 2021 15:11:31 UTC