node程序commonjs模式下,如何使用动态import语句?
发布于 作者:苏南大叔 来源:程序如此灵动~
以前曾经说过,在node程序中引入第三方模块的方式,有两种。一种是commonjs,使用require语句。另外一种是es module,使用import语句。早期的node代码,以require语句居多。目前的趋势是:抛弃require语句,使用import语句。那么,问题就来了:一些代码库只支持最新的import的方式引入代码(目前很常见)。

苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。本文记录在commonjs环境中,使用import语句的方式(本来是应该使用require的)。本文测试环境:node@16.14.2,nanoid@4.0.0。本文的龙套项目是nanoid,目前最新的版本中,已经不支持早期的require方式,可以使用npm i nanoid来进行安装。
require和import
回顾文章:https://newsn.net/say/node-run-es6.html
- 通常来说使用
node xxx.js来执行的代码,就是commonjs标准的。使用require("xxx")来导入第三方模块。 - 而使用
node xxx.mjs来执行的代码,或者package.json里面定义了type:module,或者各种编译类型(例如webpack)的,就都是属于es module类型(或者称之为es模块)。使用import("xxx")来导入第三方模块。
报错信息
由于目前越来越多的第三方模块,不再支持commonjs。所以,在使用传统的require语句的时候,就会报错。那么,代码提示可以使用dynamic import。提示信息如下:
require() of ES Module C:\Users\sunan\Desktop\demo\node_modules\nanoid\index.js from C:\Users\sunan\Desktop\demo\test.js not supported.
Instead change the require of index.js in C:\Users\sunan\Desktop\demo\test.js to a dynamic import() which is available in all CommonJS modules.关键信息:
dynamic import() which is available in all CommonJS modules.
试图在es module里面使用require()字样,也是会报错的。
ReferenceError: require is not defined in ES module scope, you can use import instead
解决方案一:await【推荐】
普通的commonjs环境下,对比如下:
const { nanoid } = require('nanoid')vs
const { nanoid } = await import('nanoid');也就是require('xxx')字样变成了await import('xxx')字样。但是,由于使用了await,大多数情况下,还需要一个async包裹一下对应的语句。
(async ()=>{
const { nanoid } = await import('nanoid');
console.log(nanoid(5));
})();
在es module里面,使用await import的时候,是不需要async字样的。

解决方案二:eval
也可以使用eval("import('xxx')")的方式。但是本例的测试中,依然需要使用await,所以没有啥特别的意义。
(async () => {
const { nanoid } = await eval("import('nanoid')");
console.log(nanoid(5));
})();或者
(async () => {
const { nanoid } = await eval(import('nanoid'));
console.log(nanoid(5));
})();
结束语
好像在任何的编程语言里面,都存在着明显的两个对立门派。另外说一句,似乎require导入比import导入自由的多,import强制要把所有的导入语句写到顶部,而commonjs里面并没有这样的要求。