【技术】当NPM包没法require的时候,我应该...

当我用 require
引入 npm
包有双叒报错的时候,我应该先拿出这篇记录瞅瞅,看看是不是这个原因。
在 js 发展过程中,国内常见的模块化方案有
规范名 | 介绍 | 网址 |
---|---|---|
AMD(Asynchronous Module Definition) | 用于客户端浏览器的 JavaScript,为异步模块加载提供了规范。 RequireJS 就是对该规范的一种实现,提供了模块加载器和构建工具。 | requirejs |
Commonjs | 用于服务器端的 JavaScript,Node.js 就是基于 CommonJS 规范的模块系统。 | Node v20.5.1 Documentation |
UMD(Universal Module Definition) | 通用模块定义,支持在 AMD、CommonJS 和全局环境中使用。 | umdjs |
ESM(ECMA Script Module) | 在 ECMAScript 6(ES6)中,定义的原生模块化,可以在支持 ES6 及以上的浏览器中直接使用。 | ECMA262-Module |
CMD(Common Module Definition) | 一个在国内比较流行的模块规范,是在 AMD 的基础上进行的一些修改和优化。比较古早的模块化框架,主要实现有 seajs,支付宝早期在用 | seajs |
如果不关注老旧版本,那只有 commonjs
和 esm
值得继续关注。
commonjs
简单规则如下(copy from chatgpt)
CommonJS 规范是一种用于 JavaScript 模块化的规范,由 Node.js 社区提出,并在 Node.js 中被广泛采用。它定义了一套通用的模块化规则和接口,使得 JavaScript 程序可以方便地编写和维护。
下面是 CommonJS 规范的主要特点和规则:
模块定义
在 CommonJS 规范中,一个模块就是一个文件,每个文件都可以看做是一个独立的模块。模块内部的所有变量和函数都是私有的,不会影响全局作用域。模块导出
在 CommonJS 规范中,一个模块可以通过 module.exports 或 exports 导出变量或函数。module.exports 是一个特殊的对象,可以用来导出模块的内容。exports 是 module.exports 的一个引用,可以用来简化导出操作。模块导入
在 CommonJS 规范中,通过 require() 函数来导入模块。require() 函数接受一个模块路径,返回一个模块对象。模块路径可以是相对路径或绝对路径,也可以是模块名,如果是模块名,则会在 node_modules 目录中查找相应的模块。模块缓存
在 CommonJS 规范中,每个模块在第一次被导入时,会被加载并执行一次,然后将其结果保存在内存中,下次导入时直接从内存中读取,而不需要重新加载和执行。这样可以提高模块加载的效率,减少重复的工作。模块循环引用
在 CommonJS 规范中,模块可以相互引用,但是如果出现循环引用,会导致模块加载和执行失败。总之,CommonJS 规范为 JavaScript 模块化提供了一套简单而有效的规则和接口,使得程序员可以更>加方便地编写和维护 JavaScript 应用程序。虽然 CommonJS 规范现在已经被 ES6 模块取代,但是>在 Node.js 中仍然得到了广泛的应用。
esm
简单规则如下(copy from chatgpt)
ESM(ECMAScript Modules)规范是 ECMAScript 6(ES6)中新增的一种模块化规范,被设计为 > JavaScript 中的原生模块系统。它提供了一种简单而有效的方式来组织和管理 JavaScript 应用程序。
下面是 ESM 规范的主要特点和规则:
模块定义
在 ESM 规范中,一个模块就是一个文件,每个文件都可以看做是一个独立的模块。模块内部的所有变量和函数都是私有的,不会影响全局作用域。模块导出
在 ESM 规范中,一个模块可以通过 export 关键字导出变量或函数。export 关键字可以在变量或函数前面使用,表示将其导出。也可以使用 export default 关键字来导出模块的默认值。模块导入
在 ESM 规范中,通过 import 关键字来导入模块。import 关键字接受一个模块路径,返回一个模块对象。模块路径可以是相对路径或绝对路径,也可以是模块名,如果是模块名,则会在 node_modules 目录中查找相应的模块。模块缓存
在 ESM 规范中,每个模块在第一次被导入时,会被加载并执行一次,然后将其结果保存在内存中,下次导入时直接从内存中读取,而不需要重新加载和执行。这样可以提高模块加载的效率,减少重复的工作。模块循环引用
在 ESM 规范中,模块可以相互引用,但是如果出现循环引用,会导致模块加载和执行失败。动态导入
在 ESM 规范中,可以使用 import() 函数来动态导入模块。import() 函数接受一个模块路径,返回一个 Promise 对象,在 Promise 对象被解析后,可以访问所导出的模块内容。总之,ESM 规范为 JavaScript 模块化提供了一套简单而有效的规则和接口,使得程序员可以更加方便地编写和维护 JavaScript 应用程序。与 CommonJS 规范相比,ESM 规范提供了更加灵活和强大的模块化功能,是未来 JavaScript 开发的趋势。
1 | /** |
虽然说只有两种需要注意,但是在今天仍然可能遇到一种情况。比如说,当了你准备引入 node-fetch
时,在文件中,写下如下内容
1 | const fetch = require('node-fetch'); |
点击运行,然后给你报错了
原以为 npm 包一定支持通过 commonjs 方式引入,但 error 信息提示,仅支持通过 esm 形式引入。
去官网一查,发现原因是
如果希望继续使用require
的方式去引入,那么就需要降级到 v2
。当然,官网也贴心的提示,v2
仍然在持续维护中。
很感人,不过我选择跟随时代的大船,用esm
,官方对于这种要求也给出了解决方案:
1 | const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args)); |
简单解析一下这个方案
- 创建一个函数,获取调用函数时候传入的所有参数:
...args
- 在函数内部通过
dynamic import
这种异步引入(es6 新增的语法)的方法,将node-fetch
包以异步方式引入 - 在成功的
then
函数中,对接收到的对外暴露对象做解构,将default
改为fetch
,并将第一步传入的所有参数,原样传递给fetch
函数并调用。
修改后运行,成功取到返回值