The SharedWorker Lifecycle Challenge in Single-page Navigation MPAs
A deep dive into maintaining SharedWorker state and connections during page transitions in MPAs
This article is mainly talking about how to maintain the connection of SharedWorker under single-page navigation MPA. If you are quite familiar with SharedWorker, you can click here to navigate to the key content.
About SharedWorker
SharedWorker is a web worker that can be shared between multiple windows. It is a powerful tool for building multi-page applications (MPAs). Here you can check out which browsers support it.
Advantages
SharedWorker provides several key benefits for multi-page applications:
-
Shared State Across Pages: Unlike regular Web Workers which are tied to a single page, SharedWorker can maintain a single instance that multiple browser contexts (tabs, windows, iframes) can connect to. This enables efficient data sharing without relying on localStorage or cookies.
-
Reduced Resource Usage: Since only one SharedWorker instance handles multiple connections, it reduces memory consumption compared to creating separate workers for each page. This is especially beneficial for resource-intensive tasks.
-
Persistent Connections: SharedWorker maintains its state and connections even when individual pages are closed, as long as at least one page is still connected. This allows for continuous background operations like WebSocket connections.
-
Improved Performance: By centralizing operations in a single worker, you can avoid duplicate requests or computations. For example, if one page fetches data, other pages can access the same data without making additional network requests. (For instance you can check out this post written by me)
SharedWorker vs. WebSocket (in the context of multi-page applications)
Capability | WebSocket (per-page) | SharedWorker + WebSocket |
---|---|---|
Connection Count | Every new tab opens its own WebSocket, so 10 tabs = 10 sockets | Exactly 1 socket for all tabs |
Server Load | 10x sockets -> 10x file descriptors, 10x heart-beats, 10x auth hand-shakes | 1x socket -> 1x everything |
Memory & CPU | Each tab keeps its own buffers, timers, JS state | One SharedWorker keeps one set of buffers & timers |
Cold-start Latency | Each tab must re-authenticate and re-subscribe to channels | First tab does the work, later tabs inherit the ready connection |
Data Deduplication | Same message is delivered N times (once per tab) | Message arrives once inside SharedWorker and is broadcast locally |
Offline/Background | Tab dies -> socket dies | Tab dies but socket stays alive until the last tab closes |
The killer advantage: SharedWorker turns an architecture that scales with the number of open pages into one that scales with the number of open browsers, slashing both client and server resource usage while keeping real-time responsiveness.
These advantages make SharedWorker particularly valuable for MPAs that need to maintain consistent state or share resources across multiple pages.
Disadvantages
Despite its benefits, SharedWorker comes with significant limitations, especially in MPA environments:
-
Connection Dependency: The most critical limitation is that SharedWorker requires at least one active port connection to remain alive. In
single-page navigation MPAs
, when users navigate from one page to another, there’s a brief moment where no page is connected to the worker. This causes the SharedWorker to terminate instantly, losing all its state and benefits. -
Broken Persistent State: Because of the connection dependency issue, the advantage of persistent connections becomes unreliable in practice. Any data stored in the SharedWorker or ongoing operations will be lost during navigation between pages, making the
single request
benefit mentioned in the advantages section largely ineffective. -
Complex Lifecycle Management: Developers must implement complex connection management to ensure the worker stays alive during page transitions. This often requires keeping track of active connections and implementing workarounds that add significant complexity to the application.
These disadvantages make SharedWorker challenging to use effectively in traditional MPAs with page-based navigation, significantly limiting its practical applications.
As you can see, the mainly problem of this must be the Connection Dependency
. If it is NOT necessary, choose multi-page navigation. That will simply keep SharedWorker alive. Or if so, dive into the following content.
Solution
I’ve spent a lot of time on this, it is just like an impossible task to adapt to most of the browsers.
Before this, I have tried using iFrame
in this post but as it said, Avoid the use of iframe’s to display the full content. So this post will explor other solutions.
Since single-page navigation MPAs
will terminate the SharedWorker when navigating locally, we need to find a way to keep the SharedWorker alive.
Prerequisites
Before diving into the solutions, it’s important to understand the conditions that must be met for SharedWorker if you are using single-page navigation MPA:
- Page Navigation Behavior: When navigating directly from one tab to a new page, the browser unloads all JavaScript scripts, DOM elements, and HTML strings from the original page before navigating to the new page. This causes the last port connected to the SharedWorker to be disconnected, terminating the worker.
So the main implementation methods are still around this point:
- Keep old port of SharedWorker’s alive until the new one is ready
The Simplest Implementation: Prerender
This is an Browser-internal optimisation hint.
Prerender is a browser feature that loads pages in the background before the user navigates to them, maintaining SharedWorker connections during page transitions.
How Prerender Works
- Pages defined in prerender rules are preloaded in advance
- SharedWorker connections are established during the prerender phase
- When the user navigates to the next page, the SharedWorker is already initialized
- This ensures continuity of shared state and connections
Using
It is quite simple to use, just list all of your MPA pages in the prerender rules.
<script type="speculationrules">
{
"prerender": [
{
"source": "list",
"urls": ["page1.html", "page2.html", ...],
"eagerness": "moderate"
}
]
}
</script>
Note: Avoid using
window.location.href='...'
because it will trigger explicit navigation so that the SharedWorker will also be terminated. A<a href="..."></a>
is recommended.
Advantages
- An available solution
- User experience is just like SPA
Limitations
So, we haven’t checked out Can I Use yet.
As you can see, it is an experimental feature by Chromium
and not widely supported. But the user base using Chromium-based browsers is up to 70%, so if you would not like to devote more time and energy to it, this is enough.
But I would not stop there.
Then I tried BFCache
, idempotent message
and so on, no one gave me the correct answer…
still exploring…