Angular 具有自定义离线页面的 PWA
Angular PWA with custom offline page
在 Angular (8) 应用程序中,我想添加一个自定义离线页面(只是一个简单的 html-文件开始)。
我已经将我的应用程序设置为 PWA(使用 @angular/pwa
并配置了所有内容,以便它至少在在线时能够顺利运行)。
但是,我很难为 PWA 用户提供更新。因此,经过数小时的尝试和错误后,我决定从 ngsw-config.json
中排除 index.html
。这 - 当然 - 每次都会加载 index.html
的效果(还不错,因为它太小了)。如果有任何更新 index.html
链接到不同的 JS 文件,这些文件会立即加载。所以,正如我之前所说,PWA 的工作方式就像我喜欢的那样。
现在我想在用户启动 PWA 离线时显示 offline.html
。所以我将 offline.html
添加到 ngsw-config.json
并且我创建了一个自定义 Service Worker 包括官方 ngsw-worker.js
:
importScripts('./ngsw-worker.js');
我也在使用这个自定义服务工作者而不是官方服务工作者:
ServiceWorkerModule.register('./custom-worker.js', { enabled: true, registrationStrategy: registrationStrategy })
到目前为止,一切仍按预期进行。行为和以前一样。现在我想在我的自定义工作器中包含离线行为:
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");
}
});
})
);
});
此脚本来自:
不幸的是,这是根本不起作用的部分。现在我几乎被困住了。我不知道下一步该怎么做。
我认为无论 index.html
是否可以加载,service worker 都会被执行。
如有任何帮助,我们将不胜感激。
index.html 和 .js 将与服务工作者一起缓存。 @angular/pwa 会为你做这件事,所以它会初始化 angular 即使离线。您可以利用它作为优势,在启动应用程序之前检查用户是否在线,以便您可以使用 APP_INITIALIZER。
首先要在应用程序初始化时调用的注册函数如下:
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 },
]
})
此服务是最初调用的服务,将包含响应应用程序初始化的方法。我刚刚在此处添加了 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();
});
}
}
请注意,您的资源 ng-sw.config 中仍需要有离线页面,以便您可以在 sw 缓存它时访问它
"resources": {
"files": [
...
"/assets/offline-page.html"
],
我成功了!
最后它是一个相对简单的 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');
在 Angular (8) 应用程序中,我想添加一个自定义离线页面(只是一个简单的 html-文件开始)。
我已经将我的应用程序设置为 PWA(使用 @angular/pwa
并配置了所有内容,以便它至少在在线时能够顺利运行)。
但是,我很难为 PWA 用户提供更新。因此,经过数小时的尝试和错误后,我决定从 ngsw-config.json
中排除 index.html
。这 - 当然 - 每次都会加载 index.html
的效果(还不错,因为它太小了)。如果有任何更新 index.html
链接到不同的 JS 文件,这些文件会立即加载。所以,正如我之前所说,PWA 的工作方式就像我喜欢的那样。
现在我想在用户启动 PWA 离线时显示 offline.html
。所以我将 offline.html
添加到 ngsw-config.json
并且我创建了一个自定义 Service Worker 包括官方 ngsw-worker.js
:
importScripts('./ngsw-worker.js');
我也在使用这个自定义服务工作者而不是官方服务工作者:
ServiceWorkerModule.register('./custom-worker.js', { enabled: true, registrationStrategy: registrationStrategy })
到目前为止,一切仍按预期进行。行为和以前一样。现在我想在我的自定义工作器中包含离线行为:
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");
}
});
})
);
});
此脚本来自:index.html
是否可以加载,service worker 都会被执行。
如有任何帮助,我们将不胜感激。
index.html 和 .js 将与服务工作者一起缓存。 @angular/pwa 会为你做这件事,所以它会初始化 angular 即使离线。您可以利用它作为优势,在启动应用程序之前检查用户是否在线,以便您可以使用 APP_INITIALIZER。
首先要在应用程序初始化时调用的注册函数如下:
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 },
]
})
此服务是最初调用的服务,将包含响应应用程序初始化的方法。我刚刚在此处添加了 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();
});
}
}
请注意,您的资源 ng-sw.config 中仍需要有离线页面,以便您可以在 sw 缓存它时访问它
"resources": {
"files": [
...
"/assets/offline-page.html"
],
我成功了!
最后它是一个相对简单的 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');