# 整合 ServiceWorker

# 文档目的

本文档旨在为开发者提供一个全面的指南,帮助开发者在应用层集成和合并 SDK 内建的 ServiceWorker 功能。并解决 ServiceWorker 冲突问题和常见问题。

虽然浏览器允许在同一个域中注册多个 service worker, 但是这些 service worker 必须拥有不同的作用域 (scope), 这样,每个 Servie Worker 才可以独立控制不同作用域下的资源。而 MessageWorker.js 占用了一个作用域(默认为 '/'), 就会导致应用层的 Service Worker 无法在相同的作用域下注册成功。所以应用层的 Service Worker 需要与SDK内置的 MessageWorker.js 共享同一个作用域时,就必须其合并为同一个文件。

本文档将介绍如何合并和自定义 Service Worker 相关的参数配置。

# ServiceWorker 简介

Service Worker 是一个运行在浏览器后台的脚本,可以用来实现离线功能、推送通知、后台同步等功能。它在网页加载完毕后,在后台运行,并可以拦截网络请求、缓存资源,并提供其他功能。

ServiceWorker 的主要功能:

  • 离线功能: ServiceWorker 可以缓存资源,即使在网络断开的情况下,用户也能访问网站。
  • 推送通知: ServiceWorker 可以接收来自服务器的推送通知,并向用户展示消息。
  • 后台同步: ServiceWorker 可以在后台执行任务,例如上传数据或更新数据。
  • 网络拦截: ServiceWorker 可以拦截网络请求,并根据需要进行处理,例如缓存数据、修改请求头等。

# MessageWorker.js 概述

MessageWorker.js 是 SDK 提供的,用于实现 Web Worker 层同步通信的 Service Worker 脚本。由于 MessageWorker.js 占用了独立的作用域,当应用层的 Service Worker 需要与其使用同一个作用域时,就需要将两者合并为同一个文件。

# 环境准备

在开始集成 Service Worker 之前,请确保你的开发环境以满足以下要求:

  • 支持 Service Worker 的浏览器: 确保你的目标浏览器支持 Service Worker, 例如: Chrome, Firefox, Edge, 具体可参考 Can I use (opens new window)
  • @foxitsoftware/foxit-pdf-sdk-for-web-library: 必须是 10.0.0 及以上的版本,并且 lib 目录下包含 MessageWorker.js 文件。
  • HTTPS 支持:您的网站必须通过 HTTPS 访问才能使用 Service Worker, 当然,开发环境例外, localhost127.0.0.1 不需要开启 HTTPS 访问也能使用 Service Worker。

# Service Worker 注册和配置

10.0.0 开始, PDFViewer 构造参数中新增了 messageSyncServiceWorker (opens new window) 参数, 用于指定 Service Worker 的注册方式。

messageSyncServiceWorker 有两种用法:

  1. 方法一:指定 urloptions:

    • url 为 Service Worker 的注册路径;
    • options 为 Service Worker 的注册选项, 可以参考 MDN (opens new window) 中的具体描述。
    const viewer = new PDFViewer({
        messageSyncServiceWorker: {
            url: '/your-service-worker.js',
            options: { // 可选
                scope: '/'
            }
        }
        // ... 其它参数
    })
    
  2. 方法二:指定 registriation:

    • registrationnavigator.serviceWorker.register() 方法返回的 Promise<ServiceWorkerRegistration> 对象。
    const viewer = new PDFViewer({
        messageSyncServiceWorker: {
            registration: navigator.serviceWorker.register('/your-service-worker.js', {
                scope: '/'
            })
        }
        // ... 其它参数
    })
    

方法一将由 SDK 内部决定何时注册 ServiceWorker。如果需要手动控制 ServiceWorker 的注册,必须使用方法二。

在SDK发布包中,我们提供了一个完整的示例(/examples/PDFViewCtrl/integrate-service-worker),您可以直接参考它的实现以及按照 README.md 文档运行和查看效果。

# Service-Worker-Allowed 响应头

在默认情况下,Service Worker 允许的最大作用范围是由其脚本位置决定的。具体来说,Service Worker 的作用范围只能覆盖其脚本所在的目录及其子目录。例如,如果 Service Worker 脚本位于 https://example.com/sub/worker.js,那么它默认只能控制 https://example.com/sub/ 及其子路径下的资源, 如果将 scope 参数强行指定为更大作用范围,则会导致 Service Worker 注册失败,并且报错: The path of the provided scope ('/') is not under the max scope allowed ('/foxit-lib/')。 然而,在某些情况下,您可能希望扩大 Service Worker 的作用范围,以便它能够控制更大范围内的资源。这时候,Service-Worker-Allowed 响应头就显得尤为重要。通过配置这个响应头,您可以指定一个更广泛的路径,使得 Service Worker 可以在更大的范围内生效。

# 配置 Service-Worker-Allowed 响应头

要使用 Service-Worker-Allowed 响应头,您需要在 Service Worker 脚本的 HTTP 响应头中添加该字段。其值为允许的最大作用范围路径:

Service-Worker-Allowed /;

# Nginx 配置示例

如果您使用 Nginx 作为服务器,可以通过修改 Nginx 配置文件来添加 Service-Worker-Allowed 响应头。以下是一个示例配置:

server {
    location /sw.js {
        add_header Service-Worker-Allowed /;
    }
}

# Webpack Dev Server 配置示例

如果您使用 Webpack Dev Server 进行本地开发,可以通过配置 devServer 添加 Service-Worker-Allowed 响应头。以下是一个配置示例:

// webpack.config.js
module.exports = {
    // 其他配置
    devServer: {
        headers: {
            'Service-Worker-Allowed': '/'
        }
    }
};

# vue.config.js 配置示例

如果您使用 Vue CLI,可以通过修改 vue.config.js 来调整 Webpack Dev Server。以下是一个配置示例:

// vue.config.js
module.exports = {
    devServer: {
        headers: {
            'Service-Worker-Allowed': '/'
        }
    }
};

# 特殊的请求地址

在应用层 Service Worker 监听的 fetch 事件中,如果请求地址匹配到 __foxitwebsdk-syncmsg__, 请直接忽略该请求。这点在示例代码(examples/PDFViewCtrl/integrate-service-worker/src/service-worker.js)中,我们也有说明。

# 常见问题和故障排查

  1. ServiceWorker 未注册

    • 问题描述:开发者工具中找不到 ServiceWorker 注册结果
    • 可能原因:
      1. 路径设置不正确, Service Worker js 请求返回 404 或其它错误;
      2. 浏览器不支持;
      3. 未启用 HTTPS 协议,为了安全,Service Worker 只能在 HTTPS 协议下使用, 例外情况是 localhost 和 127.0.0.1 不需要 https;
    • 解决方法:
      1. 检查 Service Worker 的注册代码和路径设置;
      2. 检查浏览器兼容性, Service Worker 兼容性请参考 Can I use (opens new window)
      3. 开启 HTTPS 协议。
  2. ServiceWorker 注册失败

    • 问题描述:注册时提示作用域超出最大允许范围。 错误信息示例:
        register a ServiceWorker for scope ('http://localhost:9899/') with script ('http://localhost:9899/lib/MessageWorker.js?b=http://localhost:9899/__foxitwebsdk-syncmsg__'): The path of the provided scope ('/') is not under the max scope allowed ('/foxit-lib/'). Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.
    
    • 可能原因:Service Worker的注册路径或作用域设置不正确;要注意, Service Worker 允许的最大作用范围取决于 Service Worker 脚本本身的位置(参考: MDN (opens new window))。
    • 解决方法:
      1. 理解 Service Worker 的作用域规则;
      2. 检查 Service Worker 的注册代码和作用域设置;
      3. 调整 Service Worker 的作用域, 也就是修改 scope (opens new window) 参数;
      4. 调整 Service-Worker-Allowed HTTP 响应头来允许更大的作用域;
      5. 检查 Service Worker 脚本的路径和服务器配置。
  3. ServiceWorker 注册提示 MIME 不支持

    • 问题描述: 注册时提示 unsupported MIME type ('***/***')。 错误信息示例:
    Failed to register a ServiceWorker for scope ('http://localhost:5173/assets/') with script ('http://localhost:5173/assets/service-worker.js'): The script has an unsupported MIME type ('text/html').
    
    • 可能原因: ServiceWorker 的注册路径错误或目标文件不存在。
    • 解决方法:
      1. 检查 ServiceWorker 的注册路径;
      2. 检查目标文件是否存在;
      3. 检查服务器配置。