ES6/TypeScript 动态导入 - 由于静态导入导致启动时间缓慢 (Nodejs CLI)

ES6/TypeScript Dynamic Imports - Slow launch time because of static imports (Nodejs CLI)

在我的 nodejs 代码中,我用 ES6 import { ... } from "x" 替换了许多 require(x)。 不幸的是,这对我的 nodejs-CLI-program 的启动时间非常不利。

让我先给大家介绍一下背景:

我有一个使用一大堆外部包的 nodejs-CLI 程序,但这些包中的大多数只在极少数情况下才需要(例如,如果特定标志被传递到我的程序)。 这些导入对我的程序的启动时间造成了很大的开销。 由于这些导入,即使像 myprogram --help 这样的微不足道的命令也需要超过一秒的时间。

为了解决这个启动时间问题,我想对某些功能进行“动态导入”。 换句话说,只有当给定的 CLI 命令实际需要特定功能时,我才想导入某些包。

使用旧的 require 机制,这可以通过有条件地调用 require 来实现。但是,我不确定如何为现代 TypeScript 代码执行此操作。

如果您对此类启动时间问题有任何建议,请告诉我。

您可以在 TypeScript 中使用 Dynamic Import Expressions

Dynamic import expressions are a new feature and part of ECMAScript that allows users to asynchronously request a module at any arbitrary point in your program.

This means that you can conditionally and lazily import other modules and libraries. For example, here’s an async function that only imports a utility library when it’s needed.

(在JavaScript中还是提案:https://github.com/tc39/proposal-dynamic-import)

示例:

您有一个主文件和两个依赖项。

./main.ts
./dependency-a.ts
./dependency-b.ts

依赖项 'a' 加载速度很快。

console.log('exporting dependency-a');

export const a = () => {
  console.log('called dependency-a');
};

虽然依赖项 'b' 加载缓慢。

console.log('exporting dependency-b');

// We'll emulate a slow synchronous task with a loop to add delay
// 
function wait(ms: number) {
  var start = Date.now(),
    now = start;
  while (now - start < ms) {
    now = Date.now();
  }
}

wait(5000);

export const b = () => {
  console.log('called dependency-b');
};

在您的主文件中,您有条件地调用导出函数,但由于依赖性 'b',启动时间会很慢,即使您只想调用依赖性 'a':

import { a } from './dependency-a';
import { b } from './dependency-b';

const run = (dep: 'a' | 'b') => {
  switch (dep) {
    case 'a':
      return a();

    case 'b':
      return b();

    default:
      console.log('do nothing');
  }
};

run();

您可以像这样使用动态 import() 表达式:

const run = (dep: 'a' | 'b') => {
  switch (dep) {
    case 'a':
      return import('./dependency-a').then(({ a }) => {
        a();
      });
    case 'b':
      return import('./dependency-b').then(({ b }) => {
        b();
      });
    default:
      console.log('do nothing');
  }
};

run('a');

缓慢的依赖项 'b' - 以及它的导入语句,如果有的话 - 将不会在你 运行 依赖项 'a' 时加载。这意味着您的 CLI 将有更好的启动时间。