简体   繁体   English

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

[英](Next JS 10.2) Audio Worklet Support

Our application is built on top of Next, and we are currently in the process of migrating our audio recording engine to Audio Worklet API from Script Processor.我们的应用程序构建在 Next 之上,目前我们正在将我们的录音引擎从 Script Processor 迁移到 Audio Worklet API。 (with backwards compatibility of course) Part of the transition is also upgrading to Webpack 5. We are utilizing both Web Workers and Audio Worklets. (当然具有向后兼容性)部分过渡也升级到 Webpack 5. 我们正在使用 Web Workers 和 Audio Worklets。 Prior to switch to Webpack 5, which comes with native support for Workers, (or so it appears) we used worker-plugin .在切换到 Webpack 5 之前,它带有对 Workers 的原生支持,(或者看起来如此)我们使用了worker-plugin It worked wonderfully for both, however with the transition we can no longer rely on it, because it uses outdated webpack's APIs, and Next comes with its own bundled Webpack 4 and 5, which don't seem to have backwards compatibility included.它对两者都非常有效,但是随着过渡我们不能再依赖它,因为它使用过时的 webpack 的 API,并且 Next 自带捆绑的 Webpack 4 和 5,它们似乎不包括向后兼容性。

Now the challenge is to get these working with Webpack 5 bundled with Next.现在的挑战是让这些与 Webpack 5 捆绑在一起工作。 The first issue comes from web worker's global scope being undefined.第一个问题来自 web worker 的全局 scope 未定义。 This can be resolved with by setting config.output.globalObject to "(typeof self?== 'undefined': self : this)" .这可以通过将config.output.globalObject设置为"(typeof self?== 'undefined': self : this)"来解决。 Once workers are functional, the next issue comes when trying to bundle worklet's code.一旦 worker 正常运行,下一个问题就是尝试捆绑 worklet 的代码。 Currently, Webpack 5 doesn't support worklet import, but they expose parser config, which can be used to tell webpack to load it as a worker, as per this Github issue .目前,Webpack 5 不支持工作集导入,但它们公开了解析器配置,可用于告诉 webpack 将其作为工作程序加载,如Github 问题所示 This will fail with Next unless you also set config.output.publicPath = "/_next/";除非您还设置config.output.publicPath = "/_next/"; Upon loading the chunk via audioContext.audioWorklet.addModule(...) , the code crashes with Uncaught TypeError: Cannot read property 'webpackChunk_N_E' of undefined .通过audioContext.audioWorklet.addModule(...)加载块后,代码崩溃并出现Uncaught TypeError: Cannot read property 'webpackChunk_N_E' of undefined If we inspect the chunk containing bundled worklet code, we'll see that this prop is being used in the following way:如果我们检查包含捆绑的 worklet 代码的块,我们将看到这个 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...

It's clear, that AudioWorkletGlobalScope doesn't have either self or this , so that's why it's not going too well.很明显, AudioWorkletGlobalScope没有self也没有this ,所以这就是它运行不太顺利的原因。

So the question is, how can this be worked around?所以问题是,如何解决这个问题? Audio Worklets seem to be getting very little attention still from both Next and Webpack, despite it being available by default now even in Safari . Audio Worklets 似乎仍然很少受到 Next 和 Webpack 的关注,尽管它现在默认可用,甚至在Safari中也是如此。 (since 14.5) (自 14.5 起)

Below are the code snippets representing our current state. (we are using Typescript)下面是代表我们当前 state 的代码片段。(我们使用的是 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);

Sources:资料来源:

Update更新

After much digging, seems like this issue is caused by webpack importing scripts in the chunk.经过大量挖掘,这个问题似乎是由 webpack 在块中导入脚本引起的。 Specifically, when we try to import any non-trivial code, such as lodash, or use data structures that babel deems necessary to use its shims for, Webpack injects installChunk function which uses importScripts , which obviously is not supported by AudioWorklets.具体来说,当我们尝试导入任何重要的代码(例如 lodash)或使用 babel 认为有必要使用其 shim 的数据结构时,Webpack 会注入使用importScriptsinstallChunk function,这显然不受 AudioWorklets 支持。 This also inserts the code above that causes the Uncaught TypeError: Cannot read property 'webpackChunk_N_E' of undefined .这也插入了上面导致Uncaught TypeError: Cannot read property 'webpackChunk_N_E' of undefined代码。

Here's a repo reproducing it one for one: https://github.com/thecynicalpaul/test-audio-worklet-webpack5 ( nmp i , then npm run dev )这是一个一对一复制它的 repo: https://github.com/thecynicalpaul/test-audio-worklet-webpack5 (nmp nmp i ,然后npm run dev

I was also struggling with this a lot and found that removing all the injected client code for webpack fixes the issue, without noticeable regression.我也为此苦苦挣扎,发现删除 webpack 的所有注入客户端代码可以解决问题,而不会出现明显的回归。 It's a sort of a hack, but it works:这是一种黑客攻击,但它有效:

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

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

Answering this myself, as we came to a temporary solution.当我们找到临时解决方案时,我自己回答这个问题。

The issue is happening due to the URL loader in Webpack 5 treating worklet files as if they were web worker ones.由于 URL 加载程序在 Webpack 5 中将工作集文件视为 web 工作文件,因此发生了此问题。 That means, the context is of workers, and it will attempt to use APIs such as importScripts , which are obviously not supported.这意味着,上下文是 worker 的,它将尝试使用显然不受支持的 API,例如importScripts

The solution we came to is to disable chunk splitting for any chunks that are related to worklets, effectively removing the need to use importScripts :我们得出的解决方案是禁用与工作集相关的任何块的块拆分,从而有效地消除了使用importScripts的需要:

(inside next.config.js 's webpack function) (在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;
    }
  }
}

This isn't the best solution in the grand scheme of things, for obvious reasons, and webpack's team is currently looking into a better way to handle this.出于显而易见的原因,这不是宏伟计划中的最佳解决方案,webpack 的团队目前正在寻找更好的方法来处理这个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM