Dynamic Import

import() 动态引入依赖,实现按需加载和懒加载。

背景

最近封装一个动画组件,需要根据传入的 props 按需引入依赖,一方面实现定制化能力(如:开启插件),另一方面也能减少包体积。

Static Import

众所周知 ES Modules import 是”静态“的,它只接受字符串作为模块标识符,在编译阶段就会进行依赖分析并将模块绑定到本地作用域,因此必须在文件顶部声明。虽然是常规操作,但是在一些场景并不适用:

  • 条件判断或通过请求动态引入模块。
  • 运行时计算模块标识符。
  • 在普通脚本中引入模块,而不是在模块中。
  • 完全导入整个模块,不能实现按需加载。

这时候自然就想到 AMD require() 了,同样他也有些局限:

  • 只能用于 Node.js 环境,不适用于浏览器环境。
  • 本质上只是模块声明,运行时再加载模块,而不会进行打包。

Dynamic Import

Dynamic Import 提案经过几年发展终于进入正式规范 。支持动态引入模块方案,采用函数的形式打包,并返回 Promise 对象,实现按需加载和懒加载。用法如下:

import('react').then({useState, useEffect} => {
...
});

目前主流浏览器(IE必然是非主流)、Node.js(version > 13)、webpack 等环境都已经支持该特性,可以谨慎使用。

ES2020: import() – dynamically importing ES modules

MDN: import

字符串模板

可以使用字符串模板进行动态拼接:

const redux = await import(`react-redux/dist/react-redux${isLight ? '.min' : ''}.js`);
默认模块

对于导出的默认模块,我们一般直接引入即可:

/** import */
import React from 'react';

/** export */
export default React;

然而通过 import() 引入的是个函数模块,需要对其进行解构并重命名:

/** import */
const {default as React} = await import('react');

解构重命名

Demo

  1. 通过入参 isLight 动态引入 embed(轻量级)或 oasis(重量级)渲染引擎。
  2. 通过判断 useFilters 决定是否需要开启滤镜能力。
/** 引入播放器 */
const importPlayer = async () => {
await import(`@alipay/mars-player/dist/ri_${isLight ? 'embed' : 'oasis'}.js`); // 引入渲染引擎
const {MarsPlayer, registerFilters} = await import('@alipay/mars-player');
// 开启滤镜
if (useFilters) {
const {default: filters} = await import('@alipay/mars-player/dist/filter');
registerFilters(filters);
}
return MarsPlayer;
};

/** 加载播放器 */
useEffect(() => {
importPlayer().then(MarsPlayer => {
const newPlayer: IMarsPlayer = new MarsPlayer({
interactive: true,
...constructorOptions,
container: containerRef.current,
});
setPlayer(newPlayer);
});
return () => player?.destroy(destroyOptions);
}, []);
查看评论