【问题标题】:In Node modules, how to export a module in parts so that they can be imported in pieces as needed在Node模块中,如何分块导出模块,以便根据需要分块导入
【发布时间】:2021-04-08 14:41:22
【问题描述】:

这是很难向 Google 表达的那些相当简单的问题之一:我有一个 Node 模块,我想分段导出,以便可以根据用户想要的数量分段导入它,它有对文件大小有重大影响。

我的小模块名为Piano-Notes,它导出一个 88 长的简单对象数组,描述钢琴上的每个键。

除此之外,我还下载了high-quality, public-domain samples from a Steinway,由爱荷华大学电子音乐工作室提供,每个文件大约为 10MB .aiff 文件。 (这些不包含在 repo 中,但可以通过 shell 脚本下载 - 请参阅README。然后我执行以下转换(也可作为 repo 中的 shell 和 Node 脚本):

  • 使用 ffmpeg,我将每个样本减少到 mp3 的 500 毫秒、1000 毫秒和 2000 毫秒(同时还消除了原件中的一个小的初始静默),这很有用,因为不同的应用程序需要不同的持续时间。每个音符的样本现在分别为 9KB、17KB 和 33KB。

  • 使用 Node,我将样本加载到缓冲区中,将它们转换为 base64,然后将它们捆绑到 one composite JSON file for each duration。这些文件对于 500 毫秒是 1MB,对于 1000 毫秒是 2MB,对于 2000 毫秒是 3.9MB。这消除了用户下载一堆 mp3 并弄清楚如何让模块找到它们的需要。

  • 这就是我遇到麻烦的地方:用户应该能够选择她想要的三个样本中的多少,只导入那些样本,然后将它们加载到笔记数组中,这样每个样本都有一个.play 方法。后一部分在 Notes object's .loadAudio 方法中运行良好。

问题是我的测试都在提取所有三个样本,生成 7MB 的文件。我认为这会起作用(./lib/Audio.js):

const AUDIO = {
    500: require("../data/audio/audio_500.json"),
    1000: require("../data/audio/audio_1000.json"),
    2000: require("../data/audio/audio_2000.json")
}

const Piano_500 = { duration: 500, notes: AUDIO[500] };
const Piano_1000 = { duration: 1000, notes: AUDIO[1000] };
const Piano_2000 = { duration: 2000, notes: AUDIO[2000] };

export { Piano_500, Piano_1000, Piano_2000 }

但是,我有two tests,其中一个导入所有三个,其中一个仅导入 500ms 样本:

test_all.js

import { Notes } from '../lib/Notes';
import { Piano_500, Piano_1000, Piano_2000 } from '../lib/Audio';

/* load samples into a `Notes` instance and export
export default notes;

test_500ms.js

import { Notes } from '../lib/Notes';
import { Piano_500 } from '../lib/Audio';

/* likewise */ 
export default notes;

这两个测试都会生成一个 7MB 的文件。理想情况下,第二个大约为 1MB,因为它只会加载 500 毫秒样本的 JSON 文件。

显然我没有完全理解 importsexports 的工作原理。 (我不认为我是唯一的。)我的猜测是,这是因为上面的 ./lib/Audio.js 文件需要所有这三个文件,即使它单独导出它们。如何正确编写像Audio.js 这样的导出中间件,以便一个文件可以加载模块的所有部分,根据需要将它们划分,export 这些部分可以根据需要进行import

(我认为这是处理整个项目的一种愚蠢方式,并且应该根据需要加载所有 mp3,但我仍然应该理解这一点!)

【问题讨论】:

  • 这是什么意思:“这两个测试都会产生一个 7MB 的文件。”?什么文件?你到底在测量什么?
  • test/builds中的文件是运行npm run test的产物,一个应该比另一个小很多

标签: javascript node.js import export node-modules


【解决方案1】:

在普通的 node.js 中,一个模块是一次完全导入的,无论导出中有多少个单独的入口点。将其拆分以便可以独立加载较小部分的唯一方法是将其拆分为单独的模块,以便 API 的客户端可以仅加载他们想要加载的模块。

但是,要成功地保持更小的模块负载,您必须确保您自己的模块不会每个都拉入大量其他内容或大部分您自己的其他内容。为了实现客户端可以成功地仅加载他们真正想要的东西并使其成为高效且小负载的结果,那么您需要非常仔细的内部模块设计。这是一个代码共享(有很多不同的代码段都共享东西)往往会使实现小部分加载变得非常复杂的地方,因为 API 的使用者很容易只加载一个模块,但对于在内部完成它的工作,它最终会引入你在内部共享的许多其他模块。可以做到,但需要非常仔细的设计和准确测量每个客户端导入的加载大小的能力。

【讨论】:

  • 有趣。您指的是“普通的 Node.js”。我的测试实际上是使用带有香草配置的外部 Webpack 构建,所以理想情况下会发生一些摇树,但我从未完全理解(甚至大部分理解)它是如何工作的。这相关吗?
  • @ChrisWilson - 是的,我明确使用了“plain nodejs”这个术语,因为打包程序可以尝试隔离他们构建的世界中的部分代码,但你没有提出任何问题所以我避免讨论它。但是,如果这是一个 API,那么它必须是您的 API 的用户进行正确的捆绑以仅对他们需要的部分进行 tree-shake。这不是您可以为他们做的事情,因为只有他们的环境才能指定他们正在使用和不使用什么。
  • 明白了——无论如何,我想用普通的 Node.js 来理解这一点。我将音频中间件分成三个文件,它运行正常。似乎应该有更好的解决方案,但我认为这是目前最好的方法?
  • @ChrisWilson - 这是正确的。如果最终应用程序没有支持摇树的捆绑器,则使代码的子部分可加载的唯一方法是将其分解为单独的模块文件,并在它们之间单独导出,并确保您的模块文件可以独立运行无需拉取其他代码。
猜你喜欢
  • 1970-01-01
  • 2019-03-09
  • 2019-12-31
  • 2019-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-14
相关资源
最近更新 更多