开发者问题收集

(Next JS 10.2)音频工作集支持

2021-05-08
1778

我们的应用程序建立在 Next 之上,我们目前正在将音频录制引擎从脚本处理器迁移到 Audio Worklet API。(当然具有向后兼容性)过渡的一部分也是升级到 Webpack 5。我们同时使用 Web Workers 和 Audio Worklets。在切换到 Webpack 5(它带有对 Workers 的原生支持)之前(或看起来如此),我们使用了 worker-plugin 。它对两者都非常有效,但是随着过渡,我们不再依赖它,因为它使用了过时的 webpack API,而 Next 带有自己的捆绑 Webpack 4 和 5,它们似乎没有包含向后兼容性。

现在的挑战是让它们与与 Next 捆绑的 Webpack 5 一起工作。第一个问题来自 Web Worker 的全局范围未定义。可以通过将 config.output.globalObject 设置为 "(typeof self !== 'undefined' ? self : this)" 来解决此问题。一旦工作程序正常运行,下一个问题就会出现在尝试捆绑工作集的代码时。目前,Webpack 5 不支持工作集导入,但它们公开了解析器配置,可用于告诉 webpack 将其加载为工作程序,如此 Github 问题 所述。除非您还设置了 config.output.publicPath = "/_next/"; ,否则 Next 会失败。通过 audioContext.audioWorklet.addModule(...) 加载块时,代码崩溃,出现 Uncaught TypeError: Cannot read property 'webpackChunk_N_E' of undefined 。如果我们检查包含捆绑工作单元代码的块,我们会看到此 prop 的使用方式如下:

var chunkLoadingGlobal = (typeof self !== 'undefined' ? self : this)["webpackChunk_N_E"] = (typeof self !== 'undefined' ? self : this)["webpackChunk_N_E"] || [];
var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);
.
.
.
(typeof self !== 'undefined' ? self : this)["webpackHotUpdate_N_E"] = function...

很明显, AudioWorkletGlobalScope 没有 selfthis ,所以情况不太好。

所以问题是,如何解决这个问题?尽管 Audio Worklets 现在在 Safari 中默认可用,但它似乎仍然很少受到 Next 和 Webpack 的关注。 (自 14.5 起)

下面是代表我们当前状态的代码片段。 (我们使用 Typescript)

next.config.js

module.exports = withPlugins([...], {
  future: {webpack5: true},
  webpack: (config, options) => {
    config.output.globalObject = `(typeof self !== 'undefined' ? self : this)`;
    config.output.publicPath = "/_next/";

    config.resolve = {
      ...config.resolve,
      alias: {
        ...config.resolve.alias,
        "audio-worklet": path.resolve(__dirname, "src/util/audio-worklet")
      }
    }

    config.module.parser = {
      ...config.module.parser,
      javascript: {
        worker: ["AudioWorklet from audio-worklet", "..."]
      }
    }

    config.output.chunkFilename = options.isServer
      ? `${options.dev ? "[name].[hash]" : "[name].[chunkhash]"}.js`
      : `static/chunks/${options.dev ? "[name].[hash]" : "[name].[chunkhash]"}.js`;

    return config;
  }
});

src/util/audio-worklet.ts

export const AudioWorklet = (url: URL) => {
  return url as unknown as string;
};

.../audio-context.ts

import { AudioWorklet } from "audio-worklet";

const audioContext = new AudioContext();
audioContext.audioWorklet.addModule(new AudioWorklet(new URL("path/to/processor.worklet.ts", import.meta.url)));

.../audio-worklet-processor.worklet.ts

// import statements for utility code

class Processor extends AudioWorkletProcessor {
  // Your run of the mill processor code
}

registerProcessor("processor", Processor);

来源:

更新

经过深入研究,似乎这个问题是由 webpack 在块中导入脚本引起的。具体来说,当我们尝试导入任何非平凡代码(例如 lodash)或使用 babel 认为有必要使用其垫片的数据结构时,Webpack 会注入使用 importScriptsinstallChunk 函数,这显然不受 AudioWorklets 的支持。这还会插入上述导致 Uncaught TypeError: Cannot read property 'webpackChunk_N_E' of undefined 的代码。

这里有一个一一重现它的 repo: https://github.com/thecynicalpaul/test-audio-worklet-webpack5 nmp i ,然后 npm run dev

2个回答

我也为此苦苦挣扎,发现删除所有注入的 webpack 客户端代码可以解决问题,而不会出现明显的回归。这是一种黑客行为,但它有效:

{ 
  test: /webpack-dev-server\\client/,
  loader: "null-loader"
}

来自 https://stackoverflow.com/a/41817514/2788872

John Weisz
2022-03-06

我自己来回答这个问题,因为我们找到了一个临时解决方案。

这个问题的发生是因为 Webpack 5 中的 URL 加载器将工作集文件视为 Web Worker 文件。这意味着,上下文是 Worker,它将尝试使用 importScripts 等 API,而这些 API 显然不受支持。

我们找到的解决方案是禁用与工作集相关的任何块的块拆分,从而有效地消除了使用 importScripts 的需要:

(在 next.config.js 的 webpack 函数中)

if (!options.isServer && !options.dev) {
  config.optimization.splitChunks = {
    chunks: (chunk) => {
      // this may vary widely on your loader config
      if (chunk.name && chunk.name.includes("worklet")) {
        return false;
      }

      return true;
    }
  }
}

出于显而易见的原因,这并不是总体上最好的解决方案,webpack 团队目前正在寻找更好的方法来处理这个问题。

Pavel Kozlovsky
2022-03-07