Webpack 转译的 Typescript service worker 代码似乎不起作用

Webpack transpiled Typescript service worker code doesn't seem to work

我正在尝试实现一个服务工作者,但如果使用 Webpack + Bable 将其转译为 Typescript,则无法使其执行服务工作者代码工作。注册工作正常,工作人员显示为已激活,但 installactivate 事件都没有被触发 - 我假设是由于代码未正确执行。现在,如果我用普通 Javascript 中的相同代码替换转译后的代码,一切正常。

浏览器中的其他所有内容都运行良好,因此这似乎不是我的设置存在的固有问题 - 至少 none 我理解。我真的很想知道是什么导致了这个问题!

这里是相关的代码和配置:

service-worker.ts:

declare const self: ServiceWorkerGlobalScope;

// Incrementing OFFLINE_VERSION will kick off the install event and force
//  previously cached resources to be updated from the network.
// This variable is intentionally declared and unused.
const OFFLINE_VERSION = 1;
const CACHE_NAME = "offline";
const OFFLINE_URL = "offline.html";

self.addEventListener("install", (event) => {
  console.debug("Install service worker");

  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE_NAME);
      await cache.add(new Request(OFFLINE_URL, {cache: "reload"}));

      console.debug("Service worker installed");
    })()
  );
  void self.skipWaiting();
});

self.addEventListener("activate", (event) => {
  console.debug("Activate service worker");

  event.waitUntil(
    (async () => {
      // Enable navigation preload if it's supported.
      // See https://developers.google.com/web/updates/2017/02/navigation-preload
      if ("navigationPreload" in self.registration) {
        // @ts-ignore this is only called if the browser supports it
        await self.registration.navigationPreload.enable();

        console.debug("Service worker enabled navigationPreload");
      }
    })()
  );

  void self.clients.claim();
});

self.addEventListener("fetch", (event) => {
  if (event.request.mode === "navigate") {
    event.respondWith(
      (async () => {
        try {
          // @ts-ignore
          const preloadResponse = await event.preloadResponse;
          if (preloadResponse) {
            console.debug("Returning preloaded response for fetch request");

            return preloadResponse;
          }

          const networkResponse = await fetch(event.request);

          console.debug("Returning network response for fetch request");

          return networkResponse;
        } catch (error) {
          // catch is only triggered if an exception is thrown, which is likely
          // due to a network error.
          // If fetch() returns a valid HTTP response with a response code in
          // the 4xx or 5xx range, the catch() will NOT be called.
          console.debug(`Fetch failed; returning offline page instead: ${error}`);

          const cache = await caches.open(CACHE_NAME);
          const cachedResponse = await cache.match(OFFLINE_URL);
          return cachedResponse;
        }
      })()
    );
  } else {
    console.debug("Fetch request not handled by service worker");
  }
});

export default null;

转译后的service-worker.js

"use strict";
/*
 * ATTENTION: An "eval-source-map" devtool has been used.
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
(self["webpackChunkspritstat"] = self["webpackChunkspritstat"] || []).push([["service-worker"],{

/***/ "./src/service-worker.ts":
/*!*******************************!*\
  !*** ./src/service-worker.ts ***!
  \*******************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js\");\n/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/regenerator */ \"./node_modules/@babel/runtime/regenerator/index.js\");\n/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__);\n\n\n// Incrementing OFFLINE_VERSION will kick off the install event and force\n//  previously cached resources to be updated from the network.\n// This variable is intentionally declared and unused.\nvar OFFLINE_VERSION = 1;\nvar CACHE_NAME = \"offline\";\nvar OFFLINE_URL = \"offline.html\";\nself.addEventListener(\"install\", function (event) {\n  console.debug(\"Install service worker\");\n  event.waitUntil((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {\n    var cache;\n    return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee$(_context) {\n      while (1) {\n        switch (_context.prev = _context.next) {\n          case 0:\n            _context.next = 2;\n            return caches.open(CACHE_NAME);\n\n          case 2:\n            cache = _context.sent;\n            _context.next = 5;\n            return cache.add(new Request(OFFLINE_URL, {\n              cache: \"reload\"\n            }));\n\n          case 5:\n            console.debug(\"Service worker installed\");\n\n          case 6:\n          case \"end\":\n            return _context.stop();\n        }\n      }\n    }, _callee);\n  }))());\n  void self.skipWaiting();\n});\nself.addEventListener(\"activate\", function (event) {\n  console.debug(\"Activate service worker\");\n  event.waitUntil((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee2() {\n    return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee2$(_context2) {\n      while (1) {\n        switch (_context2.prev = _context2.next) {\n          case 0:\n            if (!(\"navigationPreload\" in self.registration)) {\n              _context2.next = 4;\n              break;\n            }\n\n            _context2.next = 3;\n            return self.registration.navigationPreload.enable();\n\n          case 3:\n            console.debug(\"Service worker enabled navigationPreload\");\n\n          case 4:\n          case \"end\":\n            return _context2.stop();\n        }\n      }\n    }, _callee2);\n  }))());\n  void self.clients.claim();\n});\nself.addEventListener(\"fetch\", function (event) {\n  if (event.request.mode === \"navigate\") {\n    event.respondWith((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee3() {\n      var preloadResponse, networkResponse, cache, cachedResponse;\n      return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee3$(_context3) {\n        while (1) {\n          switch (_context3.prev = _context3.next) {\n            case 0:\n              _context3.prev = 0;\n              _context3.next = 3;\n              return event.preloadResponse;\n\n            case 3:\n              preloadResponse = _context3.sent;\n\n              if (!preloadResponse) {\n                _context3.next = 7;\n                break;\n              }\n\n              console.debug(\"Returning preloaded response for fetch request\");\n              return _context3.abrupt(\"return\", preloadResponse);\n\n            case 7:\n              _context3.next = 9;\n              return fetch(event.request);\n\n            case 9:\n              networkResponse = _context3.sent;\n              console.debug(\"Returning network response for fetch request\");\n              return _context3.abrupt(\"return\", networkResponse);\n\n            case 14:\n              _context3.prev = 14;\n              _context3.t0 = _context3[\"catch\"](0);\n              // catch is only triggered if an exception is thrown, which is likely\n              // due to a network error.\n              // If fetch() returns a valid HTTP response with a response code in\n              // the 4xx or 5xx range, the catch() will NOT be called.\n              console.debug(\"Fetch failed; returning offline page instead: \".concat(_context3.t0));\n              _context3.next = 19;\n              return caches.open(CACHE_NAME);\n\n            case 19:\n              cache = _context3.sent;\n              _context3.next = 22;\n              return cache.match(OFFLINE_URL);\n\n            case 22:\n              cachedResponse = _context3.sent;\n              return _context3.abrupt(\"return\", cachedResponse);\n\n            case 24:\n            case \"end\":\n              return _context3.stop();\n          }\n        }\n      }, _callee3, null, [[0, 14]]);\n    }))());\n  } else {\n    console.debug(\"Fetch request not handled by service worker\");\n  }\n});\n/* harmony default export */ __webpack_exports__[\"default\"] = (null);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,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QTtBQUFBO0FBQUE7QUFBQSxLQUFELElBREY7QUE2QkQsR0E5QkQsTUE4Qk87QUFDTHJCLElBQUFBLE9BQU8sQ0FBQ0MsS0FBUixDQUFjLDZDQUFkO0FBQ0Q7QUFDRixDQWxDRDtBQW9DQSwrREFBZSxJQUFmIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vc3ByaXRzdGF0Ly4vc3JjL3NlcnZpY2Utd29ya2VyLnRzPzdmNTgiXSwic291cmNlc0NvbnRlbnQiOlsiZGVjbGFyZSBjb25zdCBzZWxmOiBTZXJ2aWNlV29ya2VyR2xvYmFsU2NvcGU7XG5cbi8vIEluY3JlbWVudGluZyBPRkZMSU5FX1ZFUlNJT04gd2lsbCBraWNrIG9mZiB0aGUgaW5zdGFsbCBldmVudCBhbmQgZm9yY2Vcbi8vICBwcmV2aW91c2x5IGNhY2hlZCByZXNvdXJjZXMgdG8gYmUgdXBkYXRlZCBmcm9tIHRoZSBuZXR3b3JrLlxuLy8gVGhpcyB2YXJpYWJsZSBpcyBpbnRlbnRpb25hbGx5IGRlY2xhcmVkIGFuZCB1bnVzZWQuXG5jb25zdCBPRkZMSU5FX1ZFUlNJT04gPSAxO1xuY29uc3QgQ0FDSEVfTkFNRSA9IFwib2ZmbGluZVwiO1xuY29uc3QgT0ZGTElORV9VUkwgPSBcIm9mZmxpbmUuaHRtbFwiO1xuXG5zZWxmLmFkZEV2ZW50TGlzdGVuZXIoXCJpbnN0YWxsXCIsIChldmVudCkgPT4ge1xuICBjb25zb2xlLmRlYnVnKFwiSW5zdGFsbCBzZXJ2aWNlIHdvcmtlclwiKTtcblxuICBldmVudC53YWl0VW50aWwoXG4gICAgKGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGNhY2hlID0gYXdhaXQgY2FjaGVzLm9wZW4oQ0FDSEVfTkFNRSk7XG4gICAgICBhd2FpdCBjYWNoZS5hZGQobmV3IFJlcXVlc3QoT0ZGTElORV9VUkwsIHtjYWNoZTogXCJyZWxvYWRcIn0pKTtcblxuICAgICAgY29uc29sZS5kZWJ1ZyhcIlNlcnZpY2Ugd29ya2VyIGluc3RhbGxlZFwiKTtcbiAgICB9KSgpXG4gICk7XG4gIHZvaWQgc2VsZi5za2lwV2FpdGluZygpO1xufSk7XG5cbnNlbGYuYWRkRXZlbnRMaXN0ZW5lcihcImFjdGl2YXRlXCIsIChldmVudCkgPT4ge1xuICBjb25zb2xlLmRlYnVnKFwiQWN0aXZhdGUgc2VydmljZSB3b3JrZXJcIik7XG5cbiAgZXZlbnQud2FpdFVudGlsKFxuICAgIChhc3luYyAoKSA9PiB7XG4gICAgICAvLyBFbmFibGUgbmF2aWdhdGlvbiBwcmVsb2FkIGlmIGl0J3Mgc3VwcG9ydGVkLlxuICAgICAgLy8gU2VlIGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3dlYi91cGRhdGVzLzIwMTcvMDIvbmF2aWdhdGlvbi1wcmVsb2FkXG4gICAgICBpZiAoXCJuYXZpZ2F0aW9uUHJlbG9hZFwiIGluIHNlbGYucmVnaXN0cmF0aW9uKSB7XG4gICAgICAgIC8vIEB0cy1pZ25vcmUgdGhpcyBpcyBvbmx5IGNhbGxlZCBpZiB0aGUgYnJvd3NlciBzdXBwb3J0cyBpdFxuICAgICAgICBhd2FpdCBzZWxmLnJlZ2lzdHJhdGlvbi5uYXZpZ2F0aW9uUHJlbG9hZC5lbmFibGUoKTtcblxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiU2VydmljZSB3b3JrZXIgZW5hYmxlZCBuYXZpZ2F0aW9uUHJlbG9hZFwiKTtcbiAgICAgIH1cbiAgICB9KSgpXG4gICk7XG5cbiAgdm9pZCBzZWxmLmNsaWVudHMuY2xhaW0oKTtcbn0pO1xuXG5zZWxmLmFkZEV2ZW50TGlzdGVuZXIoXCJmZXRjaFwiLCAoZXZlbnQpID0+IHtcbiAgaWYgKGV2ZW50LnJlcXVlc3QubW9kZSA9PT0gXCJuYXZpZ2F0ZVwiKSB7XG4gICAgZXZlbnQucmVzcG9uZFdpdGgoXG4gICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgICBjb25zdCBwcmVsb2FkUmVzcG9uc2UgPSBhd2FpdCBldmVudC5wcmVsb2FkUmVzcG9uc2U7XG4gICAgICAgICAgaWYgKHByZWxvYWRSZXNwb25zZSkge1xuICAgICAgICAgICAgY29uc29sZS5kZWJ1ZyhcIlJldHVybmluZyBwcmVsb2FkZWQgcmVzcG9uc2UgZm9yIGZldGNoIHJlcXVlc3RcIik7XG5cbiAgICAgICAgICAgIHJldHVybiBwcmVsb2FkUmVzcG9uc2U7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgbmV0d29ya1Jlc3BvbnNlID0gYXdhaXQgZmV0Y2goZXZlbnQucmVxdWVzdCk7XG5cbiAgICAgICAgICBjb25zb2xlLmRlYnVnKFwiUmV0dXJuaW5nIG5ldHdvcmsgcmVzcG9uc2UgZm9yIGZldGNoIHJlcXVlc3RcIik7XG5cbiAgICAgICAgICByZXR1cm4gbmV0d29ya1Jlc3BvbnNlO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIC8vIGNhdGNoIGlzIG9ubHkgdHJpZ2dlcmVkIGlmIGFuIGV4Y2VwdGlvbiBpcyB0aHJvd24sIHdoaWNoIGlzIGxpa2VseVxuICAgICAgICAgIC8vIGR1ZSB0byBhIG5ldHdvcmsgZXJyb3IuXG4gICAgICAgICAgLy8gSWYgZmV0Y2goKSByZXR1cm5zIGEgdmFsaWQgSFRUUCByZXNwb25zZSB3aXRoIGEgcmVzcG9uc2UgY29kZSBpblxuICAgICAgICAgIC8vIHRoZSA0eHggb3IgNXh4IHJhbmdlLCB0aGUgY2F0Y2goKSB3aWxsIE5PVCBiZSBjYWxsZWQuXG4gICAgICAgICAgY29uc29sZS5kZWJ1ZyhgRmV0Y2ggZmFpbGVkOyByZXR1cm5pbmcgb2ZmbGluZSBwYWdlIGluc3RlYWQ6ICR7ZXJyb3J9YCk7XG5cbiAgICAgICAgICBjb25zdCBjYWNoZSA9IGF3YWl0IGNhY2hlcy5vcGVuKENBQ0hFX05BTUUpO1xuICAgICAgICAgIGNvbnN0IGNhY2hlZFJlc3BvbnNlID0gYXdhaXQgY2FjaGUubWF0Y2goT0ZGTElORV9VUkwpO1xuICAgICAgICAgIHJldHVybiBjYWNoZWRSZXNwb25zZTtcbiAgICAgICAgfVxuICAgICAgfSkoKVxuICAgICk7XG4gIH0gZWxzZSB7XG4gICAgY29uc29sZS5kZWJ1ZyhcIkZldGNoIHJlcXVlc3Qgbm90IGhhbmRsZWQgYnkgc2VydmljZSB3b3JrZXJcIik7XG4gIH1cbn0pO1xuXG5leHBvcnQgZGVmYXVsdCBudWxsO1xuIl0sIm5hbWVzIjpbIk9GRkxJTkVfVkVSU0lPTiIsIkNBQ0hFX05BTUUiLCJPRkZMSU5FX1VSTCIsInNlbGYiLCJhZGRFdmVudExpc3RlbmVyIiwiZXZlbnQiLCJjb25zb2xlIiwiZGVidWciLCJ3YWl0VW50aWwiLCJjYWNoZXMiLCJvcGVuIiwiY2FjaGUiLCJhZGQiLCJSZXF1ZXN0Iiwic2tpcFdhaXRpbmciLCJyZWdpc3RyYXRpb24iLCJuYXZpZ2F0aW9uUHJlbG9hZCIsImVuYWJsZSIsImNsaWVudHMiLCJjbGFpbSIsInJlcXVlc3QiLCJtb2RlIiwicmVzcG9uZFdpdGgiLCJwcmVsb2FkUmVzcG9uc2UiLCJmZXRjaCIsIm5ldHdvcmtSZXNwb25zZSIsIm1hdGNoIiwiY2FjaGVkUmVzcG9uc2UiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/service-worker.ts\n");

/***/ })

},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
/******/ __webpack_require__.O(0, ["framework-node_modules_babel_runtime_regenerator_index_js-node_modules_babel_runtime_helpers_-23c3ae"], function() { return __webpack_exec__("./src/service-worker.ts"); });
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ }
]);

index.ts service worker 注册的地方:

import "./index.sass";
import "intro.js/introjs.css";
import "intro.js/themes/introjs-modern.css"

import React from "react";
import {createRoot} from "react-dom/client";
import {BrowserRouter} from "react-router-dom";
import {Provider} from "react-redux";

import App from "./app/App";
import {store} from "./app/store";


window.addEventListener("load", async () => {
  if ("serviceWorker" in navigator) {
    console.debug("Register service worker");

    await navigator.serviceWorker.register("/service-worker.js");
  }
});

console.debug("Start application");

const container = document.getElementById("root");
const root = createRoot(container!);
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App/>
      </BrowserRouter>
    </Provider>
  </React.StrictMode>
);

package.json:

{
  "name": "test",
  "version": "0.1.0",
  "homepage": ".",
  "browser": "webpack.config.js",
  "scripts": {
    "start": "npm run trans:compile && webpack --mode=development --watch",
    "build:dev": "npm run trans:compile && webpack --mode=development",
    "build": "npm run trans:compile && webpack --mode=production",
    "db:flush": "python ../manage.py flush --noinput",
    "db:seed": "python ../manage.py loaddata",
    "test": "cypress run",
    "cy:open": "cypress open",
    "trans:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file translation/locales/de.json",
    "trans:manage": "babel-node -x .ts -- translation/manageTranslations.ts",
    "trans": "npm run trans:extract && npm run trans:manage",
    "trans:compile": "formatjs compile 'translation/locales/en.json' --ast --out-file 'src/locales/en.json'"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version",
      "last 1 opera version"
    ]
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^6.1.1",
    "@fortawesome/free-brands-svg-icons": "^6.1.1",
    "@fortawesome/free-solid-svg-icons": "^6.1.1",
    "@fortawesome/react-fontawesome": "^0.1.18",
    "@reduxjs/toolkit": "^1.8.1",
    "bulma": "^0.9.3",
    "bulma-switch": "^2.0.4",
    "chart.js": "^3.7.1",
    "chartjs-plugin-zoom": "^1.2.1",
    "intro.js": "^5.1.0",
    "intro.js-react": "^0.6.0",
    "lodash.debounce": "^4.0.8",
    "moment-timezone": "^0.5.34",
    "nanoid": "^3.3.3",
    "react": "^18.1.0",
    "react-cookie": "^4.1.1",
    "react-dom": "^18.1.0",
    "react-intl": "^5.25.1",
    "react-redux": "^8.0.1",
    "react-router-dom": "^6.3.0",
    "universal-cookie": "^4.0.4"
  },
  "devDependencies": {
    "@babel/core": "^7.17.9",
    "@babel/node": "^7.16.8",
    "@babel/plugin-transform-runtime": "^7.17.0",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "@babel/preset-typescript": "^7.16.7",
    "@babel/runtime": "^7.17.9",
    "@formatjs/cli": "^4.8.4",
    "@types/google.maps": "^3.48.7",
    "@types/intro.js": "^3.0.2",
    "@types/lodash.debounce": "^4.0.7",
    "@types/node": "^17.0.30",
    "@types/react": "^18.0.8",
    "@types/react-dom": "^18.0.3",
    "@types/react-router-dom": "^5.3.3",
    "babel-loader": "^8.2.5",
    "babel-plugin-formatjs": "^10.3.20",
    "clean-webpack-plugin": "^4.0.0",
    "compression-webpack-plugin": "^9.2.0",
    "copy-webpack-plugin": "^10.2.4",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^3.4.1",
    "cypress": "^9.6.0",
    "cypress-real-events": "^1.7.0",
    "file-loader": "^6.2.0",
    "mini-css-extract-plugin": "^2.6.0",
    "moment-locales-webpack-plugin": "^1.2.0",
    "node-sass": "^7.0.1",
    "prettier": "^2.6.2",
    "sass-loader": "^12.6.0",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.1",
    "typescript": "^4.6.4",
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2",
    "webpack-manifest-plugin": "^5.0.0"
  },
  "keywords": [],
  "author": "eega",
  "license": "MIT",
  "description": ""
}

babel.config.json:

{
  "plugins": [
    "@babel/plugin-transform-runtime",
    [
      "formatjs", {
        "idInterpolationPattern": "[sha512:contenthash:base64:6]",
        "ast": true
      }
    ]
  ],
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "ESNext",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es2017.intl",
      "es2018.intl",
      "webworker"
    ],
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src/**/*",
  ]
}

webpack.config.json:

const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const {resolve} = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const zlib = require("zlib");


module.exports = {
  devtool: "eval-source-map",
  entry: {
    app: resolve(__dirname, "src/index.tsx"),
    "service-worker": {
      import: resolve(__dirname, "src/service-worker.ts"),
      filename: "js/service-worker.js"
    }
  },
  mode: process.env.NODE_ENV ? process.env.NODE_ENV : "production",
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,

        exclude: /node_modules\/(?!react-intl|intl-messageformat|@formatjs\/icu-messageformat-parser)/,
        use: ["babel-loader"],
      },
      {
        test: /.*/,
        include: resolve(__dirname, "assets/img"),
        options: {
          context: resolve(__dirname, "assets/"),
          name: "[path][name]-[contenthash].[ext]",
        },
        loader: "file-loader",
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.sass$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  output: {
    path: resolve(__dirname, "public/"),
    filename: "js/[name]-[contenthash].js",
    chunkFilename: "js/[name]-[contenthash].chunk.js",
  },
  optimization: {
    runtimeChunk: {
      name: "runtime"
    },
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        // disable webpack's default cacheGroup
        default: false,
        // disable webpack's default vendor cacheGroup
        vendors: false,
        // Create a framework bundle that contains React libraries
        // They hardly change so we bundle them together to improve
        framework: {},
        // Big modules that are over 160kb are moved to their own file to
        // optimize browser parsing & execution
        lib: {},
        // All libraries that are used on all pages are moved into a common chunk
        commons: {},
        // When a module is used more than once we create a shared bundle to save user's bandwidth
        shared: {},
        // All CSS is bundled into one stylesheet
        styles: {}
      },
      // Keep maximum initial requests to 25
      maxInitialRequests: 25,
      // A chunk should be at least 20kb before using splitChunks
      minSize: 20000
    },
    minimizer: [
      new CompressionPlugin({
        filename: "[path][base].gz",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CompressionPlugin({
        filename: "[path][base].br",
        algorithm: "brotliCompress",
        test: /\.(js|css|html|png|svg)$/,
        compressionOptions: {
          params: {
            [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
          },
        },
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CssMinimizerPlugin(),
      new TerserPlugin(),
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new CleanWebpackPlugin(),
    new WebpackManifestPlugin({
      fileName: "webpack_manifest.json",
      publicPath: ""
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      chunkFilename: "css/[id]-[contenthash].css",
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/favicon.ico",
          to: "img",
        },
      ],
    }),
    // These are files used in the progressive web app manifest only, so they wouldn't
    //  be processed without this.
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/logo_small_*.png",
          to: "img/[name]-[contenthash].png",
          toType: "template"
        },
      ],
    }),
    new MomentLocalesPlugin({
      localesToKeep: ["de", "en"],
    })
  ],
  resolve: {
    extensions: [".js", ".jsx", ".tsx", ".ts"],
    modules: [
      resolve(__dirname, "src"),
      resolve(__dirname, "node_modules"),
    ]
  },
};

我的问题的原因是Webpack优化,即以下2个问题:

  1. 通过将 runtimeChunk 配置设置为 name: "runtime",为所有块启用了运行时提取。这导致 service worker 的运行时被提取到一个单独的块中,但这个块不是由 service worker 导入的
  2. 还通过将 splitChunks 配置选项 chunks 设置为“全部”,为服务工作者启用了块拆分,这意味着服务工作者文件也是分块的一部分,这导致转译错误

像这样更改 webpack.config.js 解决了问题:

const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const {resolve} = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const zlib = require("zlib");


module.exports = {
  devtool: "eval-source-map",
  entry: {
    app: resolve(__dirname, "src/index.tsx"),
    "service-worker": {
      import: resolve(__dirname, "src/service-worker.ts"),
      filename: "js/service-worker.js"
    }
  },
  mode: process.env.NODE_ENV ? process.env.NODE_ENV : "production",
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,

        exclude: /node_modules\/(?!react-intl|intl-messageformat|@formatjs\/icu-messageformat-parser)/,
        use: ["babel-loader"],
      },
      {
        test: /.*/,
        include: resolve(__dirname, "assets/img"),
        options: {
          context: resolve(__dirname, "assets/"),
          name: "[path][name]-[contenthash].[ext]",
        },
        loader: "file-loader",
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.sass$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  output: {
    path: resolve(__dirname, "public/"),
    filename: "js/[name]-[contenthash].js",
    chunkFilename: "js/[name]-[contenthash].chunk.js",
  },
  optimization: {
    runtimeChunk: {
      name: (entrypoint) => {
        if (entrypoint.name.startsWith("service-worker")) {
          return null;
        }

        return `runtime-${entrypoint.name}`
      }
    },
    splitChunks: {
      chunks(chunk) {
        return chunk.name !== "service-worker";
      },
      cacheGroups: {
        // disable webpack's default cacheGroup
        default: false,
        // disable webpack's default vendor cacheGroup
        vendors: false,
        // Create a framework bundle that contains React libraries
        // They hardly change so we bundle them together to improve
        framework: {},
        // Big modules that are over 160kb are moved to their own file to
        // optimize browser parsing & execution
        lib: {},
        // All libraries that are used on all pages are moved into a common chunk
        commons: {},
        // When a module is used more than once we create a shared bundle to save user's bandwidth
        shared: {},
        // All CSS is bundled into one stylesheet
        styles: {}
      },
      // Keep maximum initial requests to 25
      maxInitialRequests: 25,
      // A chunk should be at least 20kb before using splitChunks
      minSize: 20000
    },
    minimizer: [
      new CompressionPlugin({
        filename: "[path][base].gz",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CompressionPlugin({
        filename: "[path][base].br",
        algorithm: "brotliCompress",
        test: /\.(js|css|html|png|svg)$/,
        compressionOptions: {
          params: {
            [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
          },
        },
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CssMinimizerPlugin(),
      new TerserPlugin(),
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new CleanWebpackPlugin(),
    new WebpackManifestPlugin({
      fileName: "webpack_manifest.json",
      publicPath: ""
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      chunkFilename: "css/[id]-[contenthash].css",
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/favicon.ico",
          to: "img",
        },
      ],
    }),
    // These are files used in the progressive web app manifest only, so they wouldn't
    //  be processed without this.
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/logo_small_*.png",
          to: "img/[name]-[contenthash].png",
          toType: "template"
        },
      ],
    }),
    new MomentLocalesPlugin({
      localesToKeep: ["de", "en"],
    })
  ],
  resolve: {
    extensions: [".js", ".jsx", ".tsx", ".ts"],
    modules: [
      resolve(__dirname, "src"),
      resolve(__dirname, "node_modules"),
    ]
  },
};