Node中使用ES Modules规范
在 Node.js 中一般是使用的 Common.js 规范,但是从Node.js 8.5版本之后,Node.js 就已经开始去支持 ES Modules 了(实验性),从Node.js 13.2.0版本开始,Node.js 完全支持 ES Modules(ESM),所以我们可以直接在 Node.js 中使用 ES Modules。
开启使用
更改文件后缀名使用
将js文件的后缀改为.mjs即可在里面使用 ES Modules 规范。
注:如果是实验特性,在执行 node 文件时则需要添加
--experimental-modules。
开启 type=’module’使用
该方法需要的版本为 Node 12+。
在package.json里添加"type": "module"来表明该项目使用的是 ES Modules 的语法,这时就不需要将扩展名改为.mjs了。
{
"type": "module"
}
注意:如果在开启之后还想使用 CommonJS 模块的规范,需要将文件的后缀名改为
.cjs。
使用 Babel 兼容早期的 Node
对于早期的 Node.js 版本,可以使用 Babel 来实现对 ES Modules 的兼容。
Babel:目前最主流的一款 JS 的编译器,它可以将当前一些使用了新特性的代码编译成当前环境支持的代码。
添加 babel 所依赖的一些包:
yarn add @babel/node @babel/core @babel/preset-env --dev启动模块:
yarn babel-node index.js --presets=@babel/preset-envpreset-env:其实就是插件的集合,在这个集合里包含了最新的 JS 标准中所有的新特性。
如果觉得每次手动配置这个参数很麻烦,可以在配置文件
.babelrc(JSON 格式文件)添加presets配置:{ "presets": ["@babel/preset-env"] }启动:
yarn babel-node index.js由于 babel 是基于插件机制去实现的,核心模块并不会去转换我们的代码,具体转换每个代码的新特新是通过插件来实现的,一个插件来转换一个代码的特性。
但是,如果项目中只使用了某些特性,而其他不支持的特性没有使用,也可也单独下载转换特性的插件来提高效率:
移除插件集合:
yarn remove @babel/preset-env添加转换的插件:
yarn add @babel/plugin-transform-modules-commonjs --dev还有别的一些转换特性的插件,例如:
- arrow-functions:用于转换 JS 的箭头函数;
- classes:用于转换 JS 的类;
- destructuring:用于转换解构;
- module-commonjs:用于转换 ES Module;
- …
然后配置
.babelrc里的 plugins:{ "plugins": ["@babel/plugin-transform-modules-commonjs"] }启动:
yarn babel-node index.js
载入模块
可以通过 ES Modules 的方式载入模块。
载入原生模块
import fs from "fs"; fs.writeFileSync("./foo.txt", "es module working");注意:系统内置模块的成员可以被
{}提取出来,因为系统内置的模块官方都做了兼容,它会对系统内置的每一个模块的成员单独导出一次,然后再把他们作为一个对象整体做一个模块导出。import { writeFileSync } from "fs"; writeFileSync("./bar.txt", "es module working");载入第三方模块
前提是已经安装。
import _ from "lodash"; console.log(_.camelCase("ES Module"));注意:对于第三方模块,它们导出的是一个作为默认成员的对象,所以必须使用默认导入的方式去导入成员,不能使用单个命名导入。
// import { camelCase } from 'lodash' // 不可以使用,其本身语法也和对象解构无关
与使用 CommonJS 规范的对比
ES Modules 里不再提供以下全局成员变量(CommonJS 里会提供)。
// commonjs.js
// CommonJS 模块内的全局成员(Node环境下)
console.log(require); // 加载模块函数
console.log(module); // 模块对象
console.log(exports); // 导出对象别名
console.log(__filename); // 当前文件的绝对路径
console.log(__dirname); // 当前文件所在目录
对于require、module和exports可以通过 ES Modules 中的import(import())和export来代替它们导入模块,而对于__filename和__dirname可以通过import.meta.url和dirname方法代替:
import { fileURLToPath } from "url";
import { dirname } from "path";
const __filename = fileURLToPath(import.meta.url); // 将拿到的文件url转换成路径
console.log(__filename);
const __dirname = dirname(__filename); // 完整的文件路径提取出来
console.log(__dirname);
__filename:表示当前模块文件的绝对路径,包括文件名。__dirname:表示当前模块文件所在的目录的绝对路径,不包括文件名。
注:在 Node.js 中加载 CommonJS 模块里的原代码,这几个成员其实也是把当前这几个的模块包装成函数或者是形参,它实际上也是伪全局对象。(在外侧包裹一个函数,从而实现私有模块作用域)