ECMAScript模块的动态导入

544 阅读3分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

ECMAScript模块(简称ES模块)是一种JavaScript代码重用的机制

ES 模块系统有 2 个部分:

  1. 导入-import { func } from './myModule.js'
  2. 导出-export const func = () => {}。

导入模块使用import语法导入依赖项:

// The importing module
import { concat } from './concatModule.js';

concat('a', 'b'); // => 'ab'

导入的模块本身是用export语法导出的模块:

// The imported module exports components
export const concat = (paramA, paramB) => paramA + paramB;

ES 模块的方式是静态的:这意味着模块之间的依赖关系在编译时是已知的,我们不能在运行时更改导入

虽然静态导入在大多数情况下都有效,但有时也会遇到动态加载模块的情况

随着2020年推出的动态导入(dynamic imports),我们可以动态加载代码来响应用户交互

模块的动态导入

当import关键字用作函数而不是静态导入语法时:

const module = await import(path);

它返回一个 Promise 并启动一个异步任务来加载模块。如果模块成功加载,则 Promise 解析为模块内容,否则, Promise 拒绝

path可以是任何计算结果为指示路径的字符串的表达式。有效的路径表达式是:

// Classic string literals
const module1 = await import('./myModule.js');

// A variable
const path = './myOtherModule.js';
const module2 = await import(path);

// Function call
const getPath = (version) => `./myModule/versions/${version}.js`;
const moduleVersion1 = await import(getPath('v1.0'));
const moduleVersion2 = await import(getPath('v2.0'));

因为import(path)返回一个 Promise ,它非常适合async/await语法。例如,在异步函数中加载一个模块:

async function loadMyModule() {
  const myModule = await import('./myModule.js');
  // ... use myModule
}

loadMyModule();

还可以这样:

async function loadMyModule(path) {
  const myModule = await import(path);
  // ... use myModule
}

loadMyModule('./myModule.js');

导入组件

导入命名导出

这里导出一个concat 函数:

// namedConcat.js
export const concat = (paramA, paramB) => paramA + paramB;

如果想动态导入namedConcat.js 并访问命名的 export concat,那么只需通过命名的导出名称解构解析的模块对象:

async function loadMyModule() {
  const { concat } = await import('./namedConcat.js');
  concat('b', 'c'); // => 'bc'
}

loadMyModule();

导入默认导出

如果导入的模块作为默认导出,我们可以通过default 从模块对象读取属性来访问默认导入

假设defaultConcat.js导出功能作为default导出:

// defaultConcat.js
export default (paramA, paramB) => paramA + paramB;

defaultConcat.js动态导入时,特别是访问default导出时,您只需要读取default属性即可。

但有一个细微差别。default是 JavaScript 中的关键字,因此它不能用作变量名,因此只需使用带别名的解构:

async function loadMyModule() {
  const { default: defaultImport } = await import('./defaultConcat.js');
  defaultImport('b', 'c'); // => 'bc'
}

loadMyModule();

导入混合内容

如果导入的模块导出default和多个命名导出,那么也可以使用单个解构轻松访问所有这些组件:

async function loadMyModule() {
  const { 
    default: defaultImport,
    namedExport1,
    namedExport2
  } = await import('./mixedExportModule.js');
  // ...
}


loadMyModule();

何时使用动态导入

使用动态导入,我们可以拆分代码,并只在适当的时候加载重要的代码。在 JavaScript 引入动态导入之前,这种模式是webpack独有的

像React和Vue通过动态导入代码拆分来加载响应事件的代码块,比如用户交互或路由更改

建议在模块比较大时,或者需要根据某些条件才导入的模块,使用动态导入

async function execBigModule(condition) {
  if (condition) {
    const { funcA } = await import('./bigModuleA.js');
    funcA();
  } else {
    const { funcB } = await import('./bigModuleB.js');
    funcB();
  }
}

execBigModule(true);