[英]Angular PWA with custom offline page
In an Angular (8) app I'd like to add a custom offline page (just a plain simple html-file to begin with).在 Angular (8) 应用程序中,我想添加一个自定义离线页面(只是一个简单的 html 文件开始)。 I have set up my app as a PWA (using @angular/pwa
and configured everything so that it at least works smoothly while being online).我已将我的应用程序设置为 PWA(使用@angular/pwa
并配置了所有内容,以便它至少在在线时可以顺利运行)。
However, I've had a hard time making updates available for PWA users.但是,我很难为 PWA 用户提供更新。 So, after many hours of try and error I decided to exclude index.html
from the ngsw-config.json
.因此,经过数小时的尝试和错误,我决定从ngsw-config.json
index.html
This has -of course- the effect that index.html
gets loaded every single time (not so bad, 'cause it's so small).这 - 当然 - index.html
每次都会加载的效果(不是那么糟糕,因为它太小了)。 If there are any updates index.html
links to different JS-files and these files get loaded immediately.如果有任何更新index.html
链接到不同的 JS 文件,这些文件会立即加载。 So, as I said before, the PWA works just as I like it to be.所以,正如我之前所说,PWA 就像我喜欢的那样工作。
Now I want to display an offline.html
when the user starts the PWA being offline.现在我想在用户启动 PWA 离线时显示一个offline.html
。 So I've add offline.html
to ngsw-config.json
and I've created a custom Service Worker including the official ngsw-worker.js
:所以我添加了offline.html
到ngsw-config.json
并且我创建了一个自定义的 Service Worker,包括官方的ngsw-worker.js
:
importScripts('./ngsw-worker.js');
I'm also using this custom service worker instead of the official one:我也在使用这个自定义服务工作者而不是官方的:
ServiceWorkerModule.register('./custom-worker.js', { enabled: true, registrationStrategy: registrationStrategy })
So far, everything still works as expected.到目前为止,一切仍然按预期工作。 Behavior is just like before.行为和以前一样。 Now I wanted to include the offline behavior in my custom worker:现在我想在我的自定义工作者中包含离线行为:
importScripts('./ngsw-worker.js');
self.addEventListener('fetch', function(event) {
return event.respondWith(
caches.match(event.request)
.then(function(response) {
let requestToCache = event.request.clone();
return fetch(requestToCache).then().catch(error => {
// Check if the user is offline first and is trying to navigate to a web page
if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) {
// Return the offline page
return caches.match("offline.html");
}
});
})
);
});
This script comes from: https://stackoverflow.com/a/56738140/4653997 Unfortunately this is the part that doesn't work at all.该脚本来自: https://stackoverflow.com/a/56738140/4653997不幸的是,这部分根本不起作用。 For now I'm pretty much stuck.现在我几乎被困住了。 I have no idea what do next.我不知道下一步该怎么做。 I thought service workers get executed whether index.html
can get loaded or not.我认为无论index.html
是否可以加载,服务人员都会被执行。
Any help would be appreciated.任何帮助,将不胜感激。
I've got it working!我已经成功了!
In the end it was a relatively simple fetch
event listener I had to add to my custom service worker:最后,它是一个相对简单的fetch
事件监听器,我必须添加到我的自定义服务工作者中:
// listen to every fetch event self.addEventListener('fetch', function (event) { const request = event.request; // filter for html document fetches (should only result in one single fetch) --> index.html if (request.method === "GET" && request.destination === "document") { // only intercept if there was a problem fetching index.html event.respondWith( fetch(request).catch(function (error) { console.error("[onfetch] Failed. Serving cached offline fallback", error); // return offline page from cache instead return caches.match("/assets/offline.html"); })); } }); // use all the magic of the Angular Service Worker importScripts('./ngsw-worker.js');
index.html and.js will get cached with service workers. index.html 和.js 将与服务工作人员一起缓存。 @angular/pwa will do that for you so it will init angular even when offline. @angular/pwa 将为您执行此操作,因此即使在离线时它也会初始化 angular。 You can use that as advantage to check if user is online before it boots up app so you can use APP_INITIALIZER for it.您可以利用它作为优势在启动应用程序之前检查用户是否在线,以便您可以使用 APP_INITIALIZER 。
First register function that is going to be called on app init as following:首先注册 function 将在 app init 上调用,如下所示:
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { AppLoadService } from './app-load.service';
export function init_app(appLoadService: AppLoadService) {
return () => appLoadService.initializeApp();
}
@NgModule({
imports: [HttpClientModule],
providers: [
AppLoadService,
{ provide: APP_INITIALIZER, useFactory: init_app, deps: [AppLoadService], multi: true },
]
})
This service is the one that is initially called and will contain method that response to app init.此服务是最初调用的服务,将包含响应应用程序初始化的方法。 I just added window.location.href as sample here but you can do anything in that if where it checks if browser is online or not.我刚刚在此处添加了 window.location.href 作为示例,但是如果它检查浏览器是否在线,您可以执行任何操作。
export class AppLoadModule { }
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class AppLoadService {
constructor(private httpClient: HttpClient) { }
initializeApp(): Promise<any> {
return new Promise((resolve, reject) => {
if (!window.navigator.onLine) {
window.location.href = offlineURL; // this would probably be something like 'yourURL/assets/offline-page.html'
}
resolve();
});
}
}
Note that you'll still need to have offline page in your resources ng-sw.config so you can access it when sw caches it请注意,您仍然需要在资源 ng-sw.config 中有离线页面,以便在 sw 缓存它时可以访问它
"resources": {
"files": [
...
"/assets/offline-page.html"
],
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.