如何在 Angular 应用程序(Angular 7)中添加 Service Worker

如何在 Angular 应用程序(Angular 7)中添加 Service Worker

引言

Service workers 是一种很实用的技术,能够帮助我们创建离线可访问的 Web 应用程序。Service workers 允许您的 Web 应用程序发送推送通知、在后台运行脚本以及管理事务。在这篇文章中,我们将全面了解如何在 Angular 7 应用程序中添加 Service Worker。

  1. 简介

Service workers 是一个网页环境的小型的 JavaScript 程序,可以在后台执行一些任务,比如消息推送、网络请求、后台运行等。Service workers 应用程序运行在自己的进程中,拥有更高的可靠性和更快的响应速度。

Service workers 还自带缓存 API,可以实现离线存取,压缩请求,加速响应。

  1. 安装 Angular Service Worker 相关依赖

首先,你需要安装 Angular Service Worker 及其所需的库,你可以通过以下命令:

在你的 Angular 应用程序中,为了使用 Service Worker,你还需要安装 @angular/pwa:

这个命令执行后,会将 Angular 服务工人集成到你的应用程序中,准备离线支持等功能。

  1. 配置 Angular Service Worker

安装依赖后,您需要在 Angular 应用程序中配置 Service Worker。开发人员可以使用 Angular CLI 来生成一个默认的设置。为此,您需要使用以下命令:

这将自动将 Service Worker 安装并启用在你的应用中。服务工人默认从一个名为 ngsw-config.json 的 JSON 配置文件读取配置。

ngsw-config.json 是配置你 ServiceWorker 的重要文件,需要配置 cache 模式,如果需要新增一些缓存,可以继续添加 cache ,具体示例代码如下所示:

{
  "$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app",
      "installMode": "prefetch",
      "resources": {
        "files": [
          "/favicon.ico",
          "/*.css",
          "/*.js",
          "/*.jpg",
          "/*.png",
          "/*.svg"
        ],
        "urls": [
          "https://fonts.googleapis.com/css?family=Roboto:300,400,500",
          "https://fonts.googleapis.com/icon?family=Material+Icons",
          "https://fonts.gstatic.com/s/roboto/v18/KFOmCnqEu92Fr1Mu4mxP.ttf",
          "https://fonts.gstatic.com/s/materialicons/v41/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2"
        ]
      }
    },
    {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/assets/**"
        ]
      }
    }
...

由于限制篇幅,这里不再赘述每个参数的含义,具体可以参考 Angular 官方文档。

  1. 编写 Angular Service Worker

接下来是我们希望你们最喜欢的部分,写代码! 这里我们将提供以下示例代码,展示如何读取 Service Worker,为 Worker 添加 push 消息及缓存操作等。

angular-register-sw

import { Component, OnInit } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'angular-sw';
  message!: string;
  updateAvailable: boolean = false;
  constructor(private swUpdate: SwUpdate) { }

  ngOnInit(): void {
    this.updateAvailable = this.swUpdate.isEnabled;
    if (this.updateAvailable) {
      this.swUpdate.available.subscribe(() => {
        const updateMessage =
          '新版本可用,是否重新加载?';
        if (confirm(updateMessage)) {
          window.location.reload();
        }
      });
    }
    navigator.serviceWorker.addEventListener('message', event => {
      this.message = (event.data as any).message || 'no message';
    });
  }

  sendMessage() {
    navigator.serviceWorker.controller?.postMessage({
      type: 'CLIENT_MESSAGE',
      message: `Hello from the client! (${new Date().toISOString()})`
    });
  }

}

sw.js

const CACHE_NAME = 'mycache';
const CACHE_VERSION = 1;
const CACHE_PRIORITY = ['api', 'material', 'scss'];
const API_CACHE_NAME = 'netflixcoin';
const API_CACHE_REGEX = [/collection/, /coin/];

// Install our PWA
self.addEventListener('install', event => {
  console.log('Installing...');
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      cache.addAll([
        '/',
        '/index.html',
        '/favicon.ico',
      ]);
    })
  );
});

// On activate, delete old caches and log version
self.addEventListener('activate', event => {
  console.log('Activating...');
  event.waitUntil(
    caches.keys().then(keys => Promise.all([
      keys.forEach(key => {
        if (key !== CACHE_NAME) caches.delete(key);
        console.log(`Caches deleted: ${key}`);
      })

    ])).then(() => {
      console.log(`Service worker v${CACHE_VERSION} activated.`);
    })
  );
});

// Background fetch
self.addEventListener('backgroundfetchsuccess', async event => {
  console.log('Background fetch successful.');

  const cache = await caches.open(API_CACHE_NAME);

  // Filter downloaded content with regex 
  const matching = new RegExp(API_CACHE_REGEX.join('|'));
  const filteredRecords = event.fetches.filter(fetch => matching.test(fetch.request.url));
  const results = await Promise.all(filteredRecords.map(record => record.responseReady));
  await cache.addAll(results.map(r => new Response(r.body, r)));

  event.waitUntil(
    self.registration.showNotification(`Updates Available`, {
      body: `Downloaded ${results.length} items!`,
      data: 'backgroundFetch'
    })
  );

});

self.addEventListener('backgroundfetchfail', event => {
  console.log('Background Fetch failed:', event);
});

self.addEventListener('backgroundfetchabort', event => {
  console.log('Background Fetch failed:', event);
});

self.addEventListener('backgroundfetchclick', event => {
  console.log('Background fetch notification clicked:', event);
  event.waitUntil(
    clients.matchAll({
      type: 'window'
    }).then(clientList => {
      // If there is already a YouTube tab, navigate to that page.
      // Otherwise, open a new tab with the video.
      for (let i = 0; i < clientList.length; i++) {
        const client = clientList[i];
        // If we already have a window open to the site, focus it.
        if (client.url === 'http://127.0.0.1:8080/') {
          return client.focus();
        }
      }
      return clients.openWindow('/');
    })
  );
});

self.addEventListener('push', async event => {
  console.log('Push received', event.data.text());

  const client = await self.clients.get(event.source.id);
  const payload = JSON.parse(event.data.text());

  if (payload && client) {
    client.postMessage({
      message: payload.message
    });
  }
});

// Listen for incoming messages from clients
self.addEventListener('message', async event => {
  if (event.data && event.data.type === 'CLIENT_MESSAGE') {
    console.log('[Service Worker] Message received:', event.data.message);

    event.waitUntil(
      self.registration.showNotification('Title', {
        body: event.data.message,
        tag: 'new-message'
      })
    );
  }
});

// Respond with cached resources
self.addEventListener('fetch', event => {
  const cachePriority = CACHE_PRIORITY.findIndex(c => event.request.url.match(c));

  event.respondWith(
    caches.match(event.request).then(response => {
      const freshResource = fetch(event.request).then(response => {
        const responseClone = response.clone();
        caches.open(CACHE_NAME).then(cache => {
          cache.put(event.request, responseClone);
        });
        return response;
      });

      // Return cached resource or otherwise fresh resource
      return response || freshResource.catch(() => console.log('Error retrieving resource'));
    })
  );
});
  1. 运行你的 Angular Service Worker 应用程序

在添加了 Service Worker 后,你需要运行搭建程序来检查是否正常运作。可以使用以下命令运行程序:

Service worker 相关的 code 最后会被编译到 sw.js 文件,其中的 listeners会随着service worker 程序的运行而启动。

  1. 总结

以上就

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65b613a2add4f0e0ffec6851