2

我想在 Web 应用程序和服务工作者之间共享源模块(类),但找不到单独使用 typescript 的方法。使用 amd 时,只要我导入一个模块,“self”就不再在初始线程上。所有这些模块源都需要同步加载,这与我的 Web 应用程序不同,我可以在其中异步 require() 它。我尝试使用 es2015 模块而不是 amd,但遇到“无法在模块外使用 import 语句”。即使它成功了,我也会遇到与 amd 相同的问题。似乎无法绕过服务工作者使用 importScripts() 的需要。

如果我可以指示 typescript 我从 tsconfig.json 文件中针对工作人员,理论上它可以生成 serviceworker 样式的导入,但我看不到这样的支持。所以对于我的问题:

有没有办法从打字稿构建模块化服务工作者解决方案?

失败:

import { Module1 } from "./mods/module1.js";

self.addEventListener("install", (event: any) => {
  new Module1("from index");
});

我怀疑没有好的答案,因为 importScripts 不返回任何东西。这些脚本需要在 repo 中注册。如果 almond.js 有一个同步模式,我认为这会解决我的问题,因为 Typescript 会生成这个:

define(["require", "exports", "./mods/module1.js"], function (require, exports, module1_js_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    self.addEventListener("install", function (event) {
        new module1_js_1.Module1("from index");
    });
});

如果 require() 不是异步的,我可以在同一个线程上访问“self”,事情应该会顺利进行。从 almond.js 中删除 setTimeout() 会破坏加载器。

更新:有一种使用 amd 和 almond 加载器的模式:

self.importScripts("../node_modules/almond/almond.js");
self.addEventListener("install", event => {
  requirejs(["worker/index"], (worker: { run: () => void }) => worker.run());
});

我刚刚开发它,所以可能会遇到限制。worker/index.ts 看起来像这样:

import {Module1} from "../mods/module1";
export function run() {
    new Module1("running from worker");
}

因此将开发人员返回到标准打字稿模块。

更新:

以下 tsconfig.json 效果很好:

{
  "compilerOptions": {
      "outFile": "./app/service_worker.js",
    "target": "ES2015",
    "module": "amd",
    "strict": true,
    "inlineSourceMap": true
  },
  "files": ["./app/service_worker.ts","./app/serviceworker/index.ts"]
}

使用这个 package.json:

{
  "name": "pwa-poc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "server": "cd server && tsc && node server.js",
    "worker": "tsc -p tsconfig-worker.json",
    "test": "cd tests && tsc -p tsconfig.json",
    "app": "tsc -p tsconfig.json",
    "watch:worker": "npm run worker -- -w",
    "watch:test": "npm run test -- -w",
    "watch:app": "npm run app -- -w",
    "all": "npm-run-all -p server watch:worker watch:app watch:test"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "almond": "^0.3.3",
    "typescript": "^3.8.2",
    "xml2js": "^0.4.23"
  },
  "devDependencies": {
    "@types/chai": "^4.2.10",
    "@types/mocha": "^7.0.2",
    "chai": "^4.2.0",
    "mocha": "^7.1.0",
    "npm-run-all": "^4.1.5"
  }
}

重要的依赖是杏仁。生成的 index.js 包含所有 amd define 实例,almond 实现了 define 方法。

index.html 文件加载包:

<body class="theme">
    <script src="../static/almond.js"></script>
    <script src="index.js"></script>
    <script>
        window.addEventListener('load', () => {
            require(["index"], App => {
                App.run();
            });
        });
    </script>
</body>

run() 可用,因为 index.ts 导出它:

import { TableExplorer } from "./ux/TableExplorer";
import { MetaForm } from "./ux/MetaForm";
import { DataGrid } from "./ux/DataGrid";
import { saveFormData } from "./ux/saveFormData";
import { getServices } from "./fun/getServices";

export async function run() {
  navigator.serviceWorker.register("./service_worker.js");
  const params = new URLSearchParams(window.location.search);
  const table = params.get("table");
  const family = params.get("family") || "";
  const offline = !!params.get("offline");

  // ask service work to install all metadata
  const registration = await navigator.serviceWorker.ready;
  offline && registration.sync.register("offline");

  const services = getServices();

  if (!table) {
    const explorer = new TableExplorer({ services, family, useLinks: true });
    document.body.appendChild(await explorer.render());
    return;
  }

  document.title = table;
  const form = new MetaForm({ family, table, services });
  await form.render();
  document.body.appendChild(form.form);
  form.form.onsubmit = event => {
    saveFormData({ family, table, services, form });
    event.preventDefault();
    grid.refresh();
  };

  const grid = new DataGrid({ family, table, services });
  await grid.render();
  document.body.appendChild(grid.grid);
}

tsconfig-worker.json:

{
  "compilerOptions": {
      "outFile": "./app/service_worker.js",
    "target": "ES2015",
    "module": "amd",
    "strict": true,
    "inlineSourceMap": true
  },
  "files": ["./app/service_worker.ts","./app/serviceworker/index.ts"]
}

最后,service_worker.ts:

declare var requirejs: Function;

self.importScripts("../static/almond.js");
requirejs(["serviceworker/index"], (worker: { run: (worker:any) => void }) => worker.run(self));
4

1 回答 1

0

唯一的解决方案是将rolluo与插件打字稿一起使用,然后转译到systemjs,它有一个ServiceWorker兼容加载器,允许在worker中导入ESM样式,其他模块系统也可以工作,例如:AMD UMD,但这一切都捍卫了serviceworker的滚动更新概念。

有关浏览器工作人员中 ESM 支持的重要一般信息

ESM 即使在浏览器中得到支持,它本身直到今天大多数时候在工作人员和服务工作人员中都不支持,但 systemjs 是一个可以工作的 shim。

重要的一般信息为什么没有模块支持

由于 Service Worker 被设计为通过滚动更新部署,因此模块系统通常会破坏部署

所以不要使用模块总是部署一个 SingleFileServiceWorker.js

您可以为每个路径注册不同的 serviceWorker.js 文件,每个路径只能有一个。

强烈建议不惜一切代价避免从 service worker 内部的模块异步导入和下载。

于 2020-07-30T11:48:35.290 回答