- 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