Used to check for browser translation.
用于检测浏览器翻译。
ブラウザの翻訳を検出する

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 than 1.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 into self.blocklet. Therefore, it is recommended that custom service workers import this shared module importScripts('/.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 the vite-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.
    • 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. The self.blocklet.fixRouteUrl method can be used for this purpose.
    • 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.

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.

image.png

image.png

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.

image.png

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
你获得 0 积分