简体   繁体   English

Angular PWA 带自定义离线页面

[英]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.htmlngsw-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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM