为应用组件添加 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
来完成
- workbox 已经注入到
- 再接下来就是组件可以根据自身的特点来进行逻辑的处理
- 在这个步骤中,可以实现动态的资源/数据缓存,也可以实现 service-worker 与页面的通信,还可以实现 background-sync 等能力
- 在这个步骤中,大概率会使用到
self.blocklet.canCache
这个函数,它是用于判断当前请求的资源是否应该属于当前 service-worker 作用域的,通过它,可以轻松实现不同组件的资源隔离
- 在
启用/禁用组件的 service worker#
也许你会想问:等等,代码中还没有添加 service-worker 注册部分的代码吧?
是的,确实还没添加,但答案同时也是,并不需要为应用添加
只需要在应用的配置中,添加一个环境变量 ENABLED_SERVICE_WORKER
,并设置为 true
即可
重启当前组件,即可在页面中自动进行 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
所拦截
备忘单#
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