Welcome
Getting Started
How to Guides
Application vs Blocklet
Create Blocklet
Compose Blocklets
Develop Blocklet
User and Passport
Communicate with DID Wallet
Blocklet Storage
Using Blocklet Preferences
Using Blocklet Logger
Add PWA Integration to Blocklet
Build blocklet for profit [deprecated]
Bundle your blocklet
Manage Blocklet Versions
Publish your blocklet to the world
Deploy your blocklet
Read/Write blockchain in blocklet
Operation your blocklet
Reference Guides
DID Connect
blocklet.yml
blocklet.js
Blocklet SDK (Node.js)
Blocklet SDK (Browser)
Blocklet Service
Blocklet CLI
Blocklet Server CLI
Blocklet UI
Blocklet GitHub Actions
Blocklet Studio
Blocklet Manager
Security
Performance
Developer Best Practices.
Known Issues or Limitations
Setup Blocklet Server
WebHooks
OAuth Server
Access Key
MCP Servers
Conceptual Guides
Frequently Asked Questions
Add PWA Integration to Blocklet
PWA integration is currently in its early stages, and we will continue to improve the experience.
Make sure the version of@blocklet-cli
is greater than1.16.38-beta-20250115-235439-bb5a1c1b
Before we begin, let's define what is a PWA (Progressive Web App). In summary, a PWA is an app t hat is built using web platform technologies, but provides a user experience like that of a native app. Read below for a more in-depth summary.
With this foundation in place, we can now explore how to add PWA support to a Blocklet.
How does adding PWA to a Blocklet differ from a standard application?#
Because of the composable nature of blocklets, an application often contains multiple components, each with its own code and Service Worker logic.
Under these circumstances, we need a robust mechanism to isolate the Service Worker logic for each component.
Now, let's explore how to equip an existing Blocklet component with Service Worker functionality.
Adding Service Worker Code to Blocklet#
This article uses a simple Blocklet to demonstrate how to integrate a Service Worker into a Blocklet.
Add the vite-plugin-pwa
plugin and add the following code to your vite.config.js
file.#
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig(() => {
return {
// other configs
plugins: [
// other plugins
VitePWA({
manifest: false,
injectRegister: false,
registerType: 'autoUpdate',
strategies: 'injectManifest',
srcDir: 'src',
filename: 'service-worker.js',
injectManifest: {
maximumFileSizeToCacheInBytes: 4194304,
},
})
]
};
});
Add the src/service-worker.js
file and implement the service worker logic for this project.#
importScripts('/.well-known/service/static/share/shared-service-worker.js');
const { strategies, precaching, core, routing } = self.blocklet.workbox;
precaching.precacheAndRoute(
(self.__WB_MANIFEST || []).filter((x) => x.url !== 'index.html').map(self.blocklet.fixRouteUrl),
);
routing.registerRoute(
new routing.Route(
({ request, url }) => {
if (!self.blocklet.canCache({ url })) return false;
return ['image', 'font', 'style', 'script'].includes(request.destination);
},
new strategies.CacheFirst({
cacheName: `static-resources-${self.registration.scope}`,
}),
),
);
routing.registerRoute(
new routing.Route(
({ request, url }) => {
const whitelist = ['/.well-known/service/api/user/privacy/config'];
if (whitelist.includes(url.pathname)) return true;
if (!self.blocklet.canCache({ url })) return false;
return request.method === 'GET';
},
new strategies.NetworkFirst({
cacheName: `others-${self.registration.scope}`,
}),
),
);
self.skipWaiting();
core.clientsClaim();
- The
blocklet-service
provides a set of useful functions and properties, all injected intoself.blocklet
. Therefore, it is recommended that custom service workers import this shared moduleimportScripts('/.well-known/service/static/share/shared-service-worker.js');
as their first line. - The
self.__WB_MANIFEST
variable will then be used to access the static assets generated during compilation. This functionality is provided by thevite-plugin-pwa
plugin. - After the project compiles, this code snippet becomes
[{"revision":null,"url":"assets/index-BRdNHKF-.js"},{"revision":null,"url":"assets/index-CnoUf9Rp.css"},{"revision":"dc80b26617d2f88aacce576b0dd81d8a","url":"index.html"}]
, representing all the static assets for the Blocklet.
- After the project compiles, this code snippet becomes
- Then use
workbox.precaching.precacheAndRoute
to precache all the static assets obtained above. - Workbox is available directly via
self.blocklet.workbox
and does not need to be imported. - Note that static assets of Blocklet components are proxied via
.blocklet/proxy/xxx
. Therefore, when pre-caching these assets, their URLs must be transformed. Theself.blocklet.fixRouteUrl
method can be used for this purpose.
- Workbox is available directly via
- Components can then handle logic based on their individual characteristics.
- This step allows for dynamic resource/data caching, communication between the service worker and the page, and features like background sync.
- In this step, you'll likely use the
self.blocklet.canCache
function, which determines whether the requested resource falls within the current service worker's scope. This allows for straightforward resource isolation between different components.
- The
Enable/Disable a Blocklet's Service Worker#
You might be thinking: We haven't added the service worker registration code yet.
Yes, it hasn't been added yet, but this Blocklet doesn't need it.
Simply enable the service worker by setting the ENABLED_SERVICE_WORKER
environment variable to true
in the Blocklet's configuration.
Restarting the current component will automatically register the service worker.
To unregister a Blocklet's service worker, set the value of ENABLED_SERVICE_WORKER
to false
.
Tips#
Some components simply provide a service and don't require externally accessible pages.
For example, the Media Kit, which provides file uploading and browsing capabilities to other components, is often used as an image hosting service.
Due to the component isolation strategy described above, the Media Kit's resources are not cached by the current Service Worker. Therefore, you must set a specific environment variable, SERVICE_WORKER_SHARING_ENABLED
, to true
for the Media Kit. This enables shared caching within the Service Worker, preventing self.blocklet.canCache
from intercepting the Blocklet's resources.
Quick Reference#
type MountPoint = string;
type DID = string;
type BlockletServiceWorker = {
// property
workbox: Workbox;
did: DID;
prefix: string;
mountPoints: Array<MountPoint>;
sharedMountPoints: Array<MountPoint>;
// function
canCache: (options: { url: string }) => boolean;
getPrefix: () => string;
getDid: () => string;
getMountPoints: () => Array<MountPoint>;
getSharedMountPoints: () => Array<MountPoint>;
fixRouteUrl: (string) => string;
};
- workbox: Currently using
workbox-cdn
. Refer to https://developer.chrome.com/docs/workbox/modules/workbox-sw for more details. - did: The DID of the Blocklet component for this page
- prefix: The mount point of the Blocklet component for the current page
- mountPoints: All component mount points in the current Blocklet
- sharedMountPoints: The mount points for all components in the current Blocklet that have enabled
SERVICE_WORKER_SHARING_ENABLED
- canCache: Checks if a URL should be cached by the current component's service worker