Node实战:CommonJS 和 ES Module 混合开发最佳实践
前端模块化方案是比较多的,历史也比较悠久了,但是我们日常接触的最多的应该就是 CommonJS 和 ES Module 规范了,很多 npm 上的第三方包也都是基于这两种规范开发的,那么我们在使用其它的 npm 包时,就很容易产生一个项目里既有 CommonJS 又有 ES Module 的情况,那么该如何解决这种混合开发的问题呢?
一、指定使用 CommonJS 模块
1.在 node 项目里,如果不在 package.json 文件中指定 type,那这个项目默认就是 CommonJS 规范的
2.一个 .js 结尾的文件,默认是 CommonJS 规范,也可以强制的指定文件后缀为 .cjs(一般在 ES Module项目中如果希望一个文件是 CommonJS 规范的,可以这样指定后缀名)
二、指定使用 ES Module 模块
1.在 package.json 文件中指定 "type":"module" 后,这个项目就可以按照 ES Module 规范来写了
2.指定文件的后缀名为 .mjs,那么这个文件会被强制指定使用 ES Module 规范
三、CommonJS 和 ES Module 混合使用
开发中碰到的不兼容错误
1. 在 CommonJS 规范中使用了 ES Module
我们创建一个 npm 项目,新建个 js 文件,然后使用 export 或者 import 这种 ES Module 语法,运行代码时就会报下面的错误:
(node:43376) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
test.js:2
export default class A {
^^^^^^
SyntaxError: Unexpected token 'export'
原因很简单,就是:
- 未指定 package.json 的 type,默认采用 CommonJS 规范
- js 源码中使用了 ESM 语法,如 import 或 export 等
解决办法也很简单:
- 修改 js 文件后缀为 mjs
- 修改 type 的类型为 module(建议使用)
2. require 无法加载 ES Module 模块
这个也很常见,比如我们在使用一些第三方包的时候,如果我们的项目是基于 CommonJS 规范开发的,而那个包是使用 ES Module 规范开发的,使用时就会出现这个问题。 比如,使用 npm 上的 chalk 包,在 js 文件中使用 require 引入,运行时就会报错:
const chalk = require('chalk')
console.log(chalk.red('test'))
运行后报错:
const chalk = require('chalk')
^
Error [ERR_REQUIRE_ESM]: require() of ES Module
index.js from test.js not supported.
Instead change the require of index.js in
test.js to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (test.js:2:15) {
code: 'ERR_REQUIRE_ESM'
}
想要解决的话,需要通过 ES Module 规范来加载,使用 import 导入 chalk 模块
3. 最佳实践
现在越来越多的第三方包都使用 ES Module 规范来开发,所以如果不是老项目的话,尽量都采用 ES Module 规范来开发是最好的,用 ES Module 规范来兼容一些 CommonJS 规范代码。
但是在 ES Module 规范代码中,__filename, __dirname 这些变量是无法直接获取的,可以借助其它方式来使用:
- 使用 import.meta 方式获取
// filepath 就是文件的绝对路径
import { fileURLToPath } from "node:url";
const filepath = fileURLToPath(import.meta.url)
console.log('filepath: ',filepath)
- 使用第三方包 dirname-filename-esm (推荐)
import {dirname,filename} from 'dirname-filename-esm'
const __dirname = dirname(import.meta)
const __filename = filename(import.meta)
console.log('__dirname: ',__dirname)
console.log('__filename: ',__filename)
转载自:https://juejin.cn/post/7211420858213089340