Node.js 模块
在JavaScript中,一个文件就是一个模块。
模块类型
- 内置模块
Node.js自带的核心模块,无效下载安装,直接导入 - 本地模块
相对路径或绝对路径引入的模块 - 第三方模块
从npm源下载的外部模块,按规律在node_modules
目录下查找的模块
模块系统
早期JavaScript语法上没有原生支持任何模块,以致于社区通过多种方式实现了不同的JavaScript模块化方案。
在Node.js中,原生支持 CommonJS 模块系统。随着JavaScript的发展,JavaScript有了自己的ES模块系统,Node.js自14.0.0版本起支持ES模块系统(忽略奇数版本,属于测试版本)。
CommonJS 与 ES 模块对比
- 文件名
- CommonJS 文件名后缀为
.cjs
或.js
- ES 模块文件名后缀为
.mjs
,或在package.json
配置"type": "module"
,默认.js
为ES模块
- CommonJS 文件名后缀为
- 兼容
- CommonJS 不兼容导入 ES 模块
- ES 模块兼容导入 CommonJS
- 动态导入
- CommonJS、ES 模块都支持
import()
动态导入,且都支持动态导入两种模块
- CommonJS、ES 模块都支持
- 语法
- CommonJS
require()
可以在任何位置使用,并在导入位置执行,执行一次后缓存结果(按文件绝对路径缓存,大小写敏感) - ES 模块
import
在任何位置使用,都会提升到模块顶部,多次导入相当于一次导入 - CommonJS 默认导出空对象
{}
,module.exports
是修改这个空对象 - ES 模块默认导出一个空模块对象,但如果导入一个空的 CommonJS 模块,则会得到含
default
属性的对象{ default: {} }
- CommonJS 导入时可以省略
.js
后缀 - ES 模块 导入时不可省略文件名后缀
- CommonJS
CommonJS 的模块包装器函数
在 CommonJS 模块中有5个变量是在模块的局部作用域有效,可通过 console.log(arguments)
打印出来 :
exports
:对module.exports
的引用module
:模块对象require
:内部使用的require
函数__filename
:模块的绝对路径__dirname
:模块目录的绝对路径
在 Node.js 运行模块前,它会使用包装器函数包裹模块的所有代码,以实现模块作用域
js
(function(exports, require, module, __filename, __dirname) {
// Module code
});
其中,modules.paths
显示了Node.js模块的第三方模块依赖查找顺序:
即从进程当前目录向上查找 node_modules
目录下有没有匹配的第三方模块
js
module {
// ...
paths: [
// ...
'/Users/usernmae/repository/node_modules',
'/Users/usernmae/node_modules',
'/Users/node_modules',
'/node_modules'
]
}
ES 模块 import.meta
在 ES 模块中没有包装器函数,可从i
获取文件路径。
i
是一个对象,同步JavaScript规范实现,目前只有一个url
属性。
package.json
配置入口文件
在CommonJS main
字段表示入口文件的基础上,支持配置exports
来区分不同模块系统的入口文件
package.json
json
{
"name": "my-library",
"exports": {
".": {
"browser": {
"default": "./lib/browser-module.js"
}
},
"module-a": {
"import": "./path/to/module-a.mjs"
"require": "./path/to/module-a.js"
}
}
}
经过上述配置后,就能在其他包使用这个库时兼容两种模块系统使用
js
// For CommonJS
const moduleA = require('my-library/module-a')
// For ES6 Module
import moduleA from 'my-library/module-a' // 是模块名,不是文件名,没有后缀
其他写法
由于 ES 模块是JavaScript后来支持的,在规范 ES 模块的过程中,模块打包工具率先做了相关支持,但自由度较高,会出现不规范的用法,例如:ES模块可以不加文件名后缀。
其他写法虽然可以通过 Babel、Typescript 等编译成标准格式,但源码不符合规范,不推荐使用。