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

为应用组件添加 PWA 集成


在目前的版本中,PWA 的集成仍处于早期阶段,我们将持续优化 PWA 在应用中的集成体验
确保 @blocklet/cli 的版本大于 1.16.38-beta-20250115-235439-bb5a1c1b

在文章的开头,我们需要先了解什么是 PWA (Progressive Web App) ,请参考下面的链接进行了解

在有了上述的基础后,接下来可以了解一下如何在 Blocklet 中添加 PWA

在 Blocklet 中添加 PWA 与普通的应用有何不同#

由于 Blocklet 强大的可组合性,在一个应用中,往往会存在多个组件,不同的组件有自己独立的代码,也就意味着不同的组件会有自己不同的 Service Worker 逻辑。

在这样的前提下,我们必须有一个良好的机制,能够隔离各个组件的 Service Worker 逻辑。

那么接下来看看,如何将一个现有的 Blocklet 组件改造为带有 Service Worker 能力的组件。

为组件添加 Service Worker 代码#

以一个简单的应用为例,本文将逐步展示在组件中集成 Service Worker 的流程。

添加 vite-plugin-pwa 插件,并添加以下代码到 vite.config.js#

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,
},
})
]
};
});

添加 src/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();
    • blocklet-service 中提供了一组非常有用的函数和属性,均注入在 self.blocklet 中。所以自定义的 service-worker 第一行都建议导入该公共模块 importScripts('/.well-known/service/static/share/shared-service-worker.js');
    • 接下来会使用 self.__WB_MANIFEST 来获取编译时的静态资源文件,该转换由 vite-plugin-pwa 来提供
      • 在项目完成编译后,该代码片段会变成 [{"revision":null,"url":"assets/index-BRdNHKF-.js"},{"revision":null,"url":"assets/index-CnoUf9Rp.css"},{"revision":"dc80b26617d2f88aacce576b0dd81d8a","url":"index.html"}] 这样的结果,它代表了当前应用的所有静态资源
    • 接着需要使用 workbox.precaching.precacheAndRoute 来将上面获得的所有静态资源进行预缓存
      • workbox 已经注入到 self.blocklet.workbox 中了,可以直接使用,无需自己再进行导入
      • 需要注意的是:Blocklet 组件的静态资源将由 .blocklet/proxy/xxx 进行代理,所以在真正进行资源预缓存时,需要将静态资源的地址进行一次转换,可以直接使用 self.blocklet.fixRouteUrl 来完成
    • 再接下来就是组件可以根据自身的特点来进行逻辑的处理
      • 在这个步骤中,可以实现动态的资源/数据缓存,也可以实现 service-worker 与页面的通信,还可以实现 background-sync 等能力
      • 在这个步骤中,大概率会使用到 self.blocklet.canCache 这个函数,它是用于判断当前请求的资源是否应该属于当前 service-worker 作用域的,通过它,可以轻松实现不同组件的资源隔离

启用/禁用组件的 service worker#

也许你会想问:等等,代码中还没有添加 service-worker 注册部分的代码吧?

是的,确实还没添加,但答案同时也是,并不需要为应用添加

只需要在应用的配置中,添加一个环境变量 ENABLED_SERVICE_WORKER ,并设置为 true 即可

image.png

image.png

重启当前组件,即可在页面中自动进行 service-worker 的注册

如果你需要注销某个组件的 service-worker,只需要将 ENABLED_SERVICE_WORKER 的值该为 false 即可

额外的提示#

对于一些组件来说,它可能本身只是用于提供一个服务,而自身是不需要对外提供可访问页面的

比如 Media Kit,它的作用就是为其他组件提供文件上传和浏览的能力,常常会作为图床来使用

基于上面的组件隔离策略,Media Kit 中的资源则不会被当前的 Service Worker 进行缓存,那么此时就需要为 Media Kit 设置一个特殊的环境变量,SERVICE_WORKER_SHARING_ENABLED 将值设置为 true,表示允许在 Service Worker 进行共享缓存,此时该组件的资源就不会被 self.blocklet.canCache 所拦截

image.png

备忘单#

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: 目前使用的是 workbox-cdn ,查询 https://developer.chrome.com/docs/workbox/modules/workbox-sw 获取更多相关信息
  • did: 当前访问的页面对应的 Blocklet 组件的 did
  • prefix: 当前访问的页面对应的 Blocklet 组件的 mountPoint
  • mountPoints: 当前应用中所有组件的 mountPoint
  • sharedMountPoints: 当前应用中所有开启了 SERVICE_WORKER_SHARING_ENABLED 组件的 mountPoint
  • canCache: 检查一个 url 是否建议被当前组件的 service worker
你获得 0 积分