使用 Sveltekit 创建 PWA | Svelte
我使用 sveltekit 和 svelte 已经一年多了。我也在等待它足够成熟,能够在社区方面超越 NextJs。不过我都很喜欢它们。
所以,这个周末我想把我的一个 SvelteKit 项目改造成 PWA。当我想把 NextJs 项目改造成 PWA 时,有很多教程。但我找不到很多针对 Svelte 初学者的指南。
这是因为 svelte 内置了 pwa 功能。
笔记 !
所有 PWA 的基本内容
- 一个网站
- manifest.json [基本图标、名称、快捷方式]
- service-worker [ 用于离线缓存 ]
那么让我们继续吧。
第一的:
我们将创建一个演示 Sveltekit 项目:
npm init svelte@next my-app
然后为了本文的目的,我们将在邀请中选择一个简单的配置:
选择 TypeScript 是因为#typescriptgang
:
现在我们已经用 TypeScript 设置了一个演示项目,从这里开始一切都会变得简单直接:
让我们进入目录:
cd my-app
然后运行:
yarn
在那之后,
- 在 /static 目录中,我们将创建一个 manifest.json。
- 当 svelte 编译整个应用程序时,它会将静态文件复制到构建文件夹。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
{ | |
"short_name": "svelte-pwa", | |
"name": "svelte-test-pwa", | |
"start_url": "/", | |
"icons": [ | |
{ | |
"src": "logo_512.png", | |
"type": "image/png", | |
"sizes": "512x512" | |
} | |
], | |
"background_color": "#3367D6", | |
"display": "standalone", | |
"scope": "/", | |
"theme_color": "#3367D6", | |
"shortcuts": [ | |
{ | |
"name": "How's weather today?", | |
"short_name": "Today", | |
"description": "View weather information for today", | |
"url": "/today?source=pwa", | |
"icons": [{ "src": "/icons/logo_192.png", "sizes": "192x192" }] | |
}, | |
{ | |
"name": "How's weather tomorrow?", | |
"short_name": "Tomorrow", | |
"description": "View weather information for tomorrow", | |
"url": "/tomorrow?source=pwa", | |
"icons": [{ "src": "/icons/logo_192.png", "sizes": "192x192" }] | |
} | |
], | |
"description": "Lofi to lofi beats, while using the scratchpad to list down tasks and thoughts" | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
{ | |
"short_name": "svelte-pwa", | |
"name": "svelte-test-pwa", | |
"start_url": "/", | |
"icons": [ | |
{ | |
"src": "logo_512.png", | |
"type": "image/png", | |
"sizes": "512x512" | |
} | |
], | |
"background_color": "#3367D6", | |
"display": "standalone", | |
"scope": "/", | |
"theme_color": "#3367D6", | |
"shortcuts": [ | |
{ | |
"name": "How's weather today?", | |
"short_name": "Today", | |
"description": "View weather information for today", | |
"url": "/today?source=pwa", | |
"icons": [{ "src": "/icons/logo_192.png", "sizes": "192x192" }] | |
}, | |
{ | |
"name": "How's weather tomorrow?", | |
"short_name": "Tomorrow", | |
"description": "View weather information for tomorrow", | |
"url": "/tomorrow?source=pwa", | |
"icons": [{ "src": "/icons/logo_192.png", "sizes": "192x192" }] | |
} | |
], | |
"description": "Lofi to lofi beats, while using the scratchpad to list down tasks and thoughts" | |
} |
然后我们将参考我们manifest.json
的src/app.html
。
最后我们将创建我们的src/service-worker.ts
Svelte 会自动检测 src 文件夹根目录中的 service-worker,然后在 build 期间注册我们的服务工作线程。
是不是很棒?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
/// <reference lib="webworker" /> | |
import { build, files, timestamp } from '$service-worker'; | |
const worker = (self as unknown) as ServiceWorkerGlobalScope; | |
const FILES = `cache${timestamp}`; | |
// `build` is an array of all the files generated by the bundler, | |
// `files` is an array of everything in the `static` directory | |
const to_cache = build.concat(files); | |
const staticAssets = new Set(to_cache); | |
worker.addEventListener('install', (event) => { | |
event.waitUntil( | |
caches | |
.open(FILES) | |
.then((cache) => cache.addAll(to_cache)) | |
.then(() => { | |
worker.skipWaiting(); | |
}) | |
); | |
}); | |
worker.addEventListener('activate', (event) => { | |
event.waitUntil( | |
caches.keys().then(async (keys) => { | |
// delete old caches | |
for (const key of keys) { | |
if (key !== FILES) await caches.delete(key); | |
} | |
worker.clients.claim(); | |
}) | |
); | |
}); | |
/** | |
* Fetch the asset from the network and store it in the cache. | |
* Fall back to the cache if the user is offline. | |
*/ | |
async function fetchAndCache(request: Request) { | |
const cache = await caches.open(`offline${timestamp}`); | |
try { | |
const response = await fetch(request); | |
cache.put(request, response.clone()); | |
return response; | |
} catch (err) { | |
const response = await cache.match(request); | |
if (response) return response; | |
throw err; | |
} | |
} | |
worker.addEventListener('fetch', (event) => { | |
if (event.request.method !== 'GET' || event.request.headers.has('range')) return; | |
const url = new URL(event.request.url); | |
// don't try to handle e.g. data: URIs | |
const isHttp = url.protocol.startsWith('http'); | |
const isDevServerRequest = | |
url.hostname === self.location.hostname && url.port !== self.location.port; | |
const isStaticAsset = url.host === self.location.host && staticAssets.has(url.pathname); | |
const skipBecauseUncached = event.request.cache === 'only-if-cached' && !isStaticAsset; | |
if (isHttp && !isDevServerRequest && !skipBecauseUncached) { | |
event.respondWith( | |
(async () => { | |
// always serve static files and bundler-generated assets from cache. | |
// if your application has other URLs with data that will never change, | |
// set this variable to true for them and they will only be fetched once. | |
const cachedAsset = isStaticAsset && (await caches.match(event.request)); | |
return cachedAsset || fetchAndCache(event.request); | |
})() | |
); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
/// <reference lib="webworker" /> | |
import { build, files, timestamp } from '$service-worker'; | |
const worker = (self as unknown) as ServiceWorkerGlobalScope; | |
const FILES = `cache${timestamp}`; | |
// `build` is an array of all the files generated by the bundler, | |
// `files` is an array of everything in the `static` directory | |
const to_cache = build.concat(files); | |
const staticAssets = new Set(to_cache); | |
worker.addEventListener('install', (event) => { | |
event.waitUntil( | |
caches | |
.open(FILES) | |
.then((cache) => cache.addAll(to_cache)) | |
.then(() => { | |
worker.skipWaiting(); | |
}) | |
); | |
}); | |
worker.addEventListener('activate', (event) => { | |
event.waitUntil( | |
caches.keys().then(async (keys) => { | |
// delete old caches | |
for (const key of keys) { | |
if (key !== FILES) await caches.delete(key); | |
} | |
worker.clients.claim(); | |
}) | |
); | |
}); | |
/** | |
* Fetch the asset from the network and store it in the cache. | |
* Fall back to the cache if the user is offline. | |
*/ | |
async function fetchAndCache(request: Request) { | |
const cache = await caches.open(`offline${timestamp}`); | |
try { | |
const response = await fetch(request); | |
cache.put(request, response.clone()); | |
return response; | |
} catch (err) { | |
const response = await cache.match(request); | |
if (response) return response; | |
throw err; | |
} | |
} | |
worker.addEventListener('fetch', (event) => { | |
if (event.request.method !== 'GET' || event.request.headers.has('range')) return; | |
const url = new URL(event.request.url); | |
// don't try to handle e.g. data: URIs | |
const isHttp = url.protocol.startsWith('http'); | |
const isDevServerRequest = | |
url.hostname === self.location.hostname && url.port !== self.location.port; | |
const isStaticAsset = url.host === self.location.host && staticAssets.has(url.pathname); | |
const skipBecauseUncached = event.request.cache === 'only-if-cached' && !isStaticAsset; | |
if (isHttp && !isDevServerRequest && !skipBecauseUncached) { | |
event.respondWith( | |
(async () => { | |
// always serve static files and bundler-generated assets from cache. | |
// if your application has other URLs with data that will never change, | |
// set this variable to true for them and they will only be fetched once. | |
const cachedAsset = isStaticAsset && (await caches.match(event.request)); | |
return cachedAsset || fetchAndCache(event.request); | |
})() | |
); | |
} | |
}); |
现在我们只需要使用以下命令构建我们的应用程序yarn build
:
现在我们可以使用以下方式预览我们的构建yarn preview
:
😯 这是“安装应用程序”按钮,
Svelte 使制作 PWA 变得容易。
该项目的源代码位于:
https://github.com/100lvlmaster/svelte-pwa
您可以在以下位置找到我:
鏂囩珷鏉ユ簮锛�https://dev.to/100lvlmaster/create-a-pwa-with-sveltekit-svelte-a36