VitePWA Как создать offline.html страницу?
Я пишу игру https://colobit.ru на react+ts+vite. Чтобы игру можно было установить, как нативное приложение, я использую плагин vitePWA и workbox. Недавно я сделал из приложения с помощью TWA apk файл и хотел опубликовать игру в RuStore, но там нельзя этого делать, тк я использую webView без поддержки оффлайн
Как мне добавить оффлайн поддержку? Я много раз пытался сделать это сам, но для меня это какая-то нереальная задача. Вот ни при каких условиях не работает у меня код, который должен в оффлайн работать.
Вот мой vite.config.ts
import {defineConfig} from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
import path from 'path';
import {VitePWA} from "vite-plugin-pwa";
const manifest = {
"display": "standalone",
"scope": "/",
"start_url": "/farm",
"name": "ColorBit",
// ...
"icons": [
{
"src": "icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
// ...
]
};
const getCache = ({name, pattern, strategy = "CacheFirst"}: any) => ({
urlPattern: pattern,
handler: strategy,
options: {
cacheName: name,
expiration: {
maxEntries: 500,
maxAgeSeconds: 60 * 60 * 24 * 60 // 2 months
},
cacheableResponse: {
statuses: [0, 200]
}
}
});
export default defineConfig({
plugins: [
laravel({
input: ['resources/js/app.tsx',],
refresh: true,
}),
react({
fastRefresh: false
}),
VitePWA({
registerType: 'autoUpdate',
outDir: path.resolve(__dirname, 'public'),
manifest: manifest,
manifestFilename: 'manifest.webmanifest', // Change name for app manifest
injectRegister: false, // I register SW in app.ts, disable auto registration
srcDir: path.resolve(__dirname, 'resources/js/'),
filename: 'serviceWorker.js',
strategies: 'injectManifest',
workbox: {
globDirectory: path.resolve(__dirname, 'public'), // Directory for caching
globPatterns: [
'{build,images,sounds,icons}/**/*.{js,css,html,ico,png,jpg,mp4,svg}'
],
},
})
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'resources/js'),
// ...
},
extensions: ['.js', '.ts', '.tsx', '.jsx'],
},
});
serviceWorker.js
import {ExpirationPlugin} from 'workbox-expiration';
import {createHandlerBoundToURL, precacheAndRoute, cleanupOutdatedCaches} from 'workbox-precaching';
import {registerRoute} from 'workbox-routing';
import {CacheFirst} from 'workbox-strategies';
import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
// Register precache routes (static cache)
precacheAndRoute(self.__WB_MANIFEST || []);
// Clean up old cache
cleanupOutdatedCaches();
// Google fonts dynamic cache
registerRoute(
/^https:\/\/fonts\.googleapis\.com\/.*/i,
new CacheFirst({
cacheName: "google-fonts-cache",
plugins: [
new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
new CacheableResponsePlugin({statuses: [0, 200]})
]
}), "GET");
// Google fonts dynamic cache
registerRoute(
/^https:\/\/fonts\.gstatic\.com\/.*/i, new CacheFirst({
cacheName: "gstatic-fonts-cache",
plugins: [
new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
new CacheableResponsePlugin({statuses: [0, 200]})
]
}), "GET");
// Dynamic cache for images from `/storage/`
registerRoute(
/.*storage.*/, new CacheFirst({
cacheName: "dynamic-images-cache",
plugins: [
new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
new CacheableResponsePlugin({statuses: [0, 200]})
]
}), "GET");
// Install and activate service worker
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', () => self.clients.claim());
// Receive push notifications
self.addEventListener('push', function (e) {
if (!(
self.Notification &&
self.Notification.permission === 'granted'
)) {
// notifications aren't supported or permission not granted!
console.log('Notifications aren\'t supported or permission not granted!')
return;
}
if (e.data) {
let message = e.data.json();
e.waitUntil(self.registration.showNotification(message.title, {
body: message.body,
icon: message.icon,
actions: message.actions
}));
}
});
// Click and open notification
self.addEventListener('notificationclick', function(event) {
event.notification.close();
if (event.action === 'farm') clients.openWindow("/farm");
// ...
else clients.openWindow('/farm'); // Open link from action
}, false);
Нужные файлы действительно кэшируется, но когда я включаю оффлайн режим, у меня стандартная заглука от браузера.
Как сделать так, чтобы при отсутствии интернета пользователю возвращалась страница offline.html?
То есть https://colobit.ru/offline.html
Ответы (1 шт):
Методом проб и ошибок удалось решить проблему. Вот мой service-worker.js
import {ExpirationPlugin} from 'workbox-expiration';
import {createHandlerBoundToURL, precacheAndRoute, cleanupOutdatedCaches} from 'workbox-precaching';
import {registerRoute} from 'workbox-routing';
import {CacheFirst} from 'workbox-strategies';
import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
// Register precache routes (static cache)
precacheAndRoute(self.__WB_MANIFEST || []);
// Clean up old cache
cleanupOutdatedCaches();
// Google fonts dynamic cache
registerRoute(
/^https:\/\/fonts\.googleapis\.com\/.*/i,
new CacheFirst({
cacheName: "google-fonts-cache",
plugins: [
new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
new CacheableResponsePlugin({statuses: [0, 200]})
]
}), "GET");
// Dynamic cache for images from `/storage/`
registerRoute(
/.*storage.*/, new CacheFirst({
cacheName: "dynamic-images-cache",
plugins: [
new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
new CacheableResponsePlugin({statuses: [0, 200]})
]
}), "GET");
// Install and activate service worker
self.addEventListener('activate', () => self.clients.claim());
const offlineCacheName = 'offline-cache';
const offlineCacheFiles = [
'/offline/offline.html',
'/offline/error.png'
]
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(offlineCacheName).then((cache) => {
return cache.addAll(offlineCacheFiles);
})
);
});
// Fetch event
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then(async (response) => {
try {
// If there's a cache response, return it
// Or try to fetch it using the Internet
return response || fetch(event.request).catch(() => {
// If the request fails (offline), return the offline.html page
return caches.match('/offline/offline.html');
});
} catch (e) {
return caches.match('/offline/offline.html');
}
})
);
});

