[模块]ES6与cjs的混合开发
- 模块语言
- 混合开发的原因
- Nodejs中使用ES6
- 项目的模块语言
- [CJS 与 ESM 开发](#CJS 与 ESM 开发)
- 问题
-
- [Error: EPERM: operation not permitted, rename](#Error: EPERM: operation not permitted, rename)
- [SyntaxError: Cannot use import statement outside a module](#SyntaxError: Cannot use import statement outside a module)
- [Error: Cannot find module 'D:\serverjs\index'](#Error: Cannot find module 'D:\serverjs\index')
模块语言
现有的模块化开发语言主要包括:
- CJS(CommonJS):同步的模块化规范
- ESM-是 JavaScript 语言层面的模块规范,目标是取代其它所有模块化开发规范,成为前端领域的标准模块规范
因为 ESM 是下一代的标准,所以开发的时候尽量使用ESM
混合开发的原因
因为最早使用的CJS(CommonJS)规范开发,后来随着ES6规范的开始,大部分的库陆续转化成ES6的规范,但是部分仍旧是使用的CJS的规范,在一个ESM的项目中使用了CJS的模块,就导致了项目的混合开发
Nodejs中使用ES6
在 Node.js 中使用 ES6 的 import
/ export
与 require
的区别主要体现在以下几个方面:
import
/export
是 ES6 的模块化语法,而require
是 Node.js 的模块化语法import
/export
是静态的,只能在模块的顶层使用,而require
是动态的,可以在任何地方使用import
/export
可以导出多个变量、函数等,而require
只能导出一个对象import
/export
是异步加载模块,而require
是同步加载模块
总的来说,如果你使用的是 ES6 的语法,建议使用 import
/ export
,否则使用 require
,但是如上所说ESM 是下一代的标准,所以逐渐的nodejs使用ES6的方式越来越方便
关于动态加载的讲解
项目的模块语言
package.json 中的 type 字段用来指定项目的类型,默认为commonjs。type的取值包括:
commonjs
: 表示该包是一个 CommonJS 模块(即 Node.js 默认的模块格式)module
: 表示该包是一个 ES 模块(即标准的 ECMAScript 模块格式)json
: 表示该包仅包含 JSON 数据,可以通过 require() 函数或 ES6 的 import 语句来导入
在项目中,不同文件的后缀表明:
.cjs
是使用CommonJS规范
.mjs
是使用ES规范
.js
则以package.json中的type字段为准
所以可以通过以上不同的文件后缀来实现项目的混合开发
CJS 与 ESM 开发
CJS 与 ESM规范在使用上有非常多的不同之处,下面是本人一个CJS的项目升级到ESM规范的过程中遇到的各种问题
模块的使用方法
CJS通过require引入,module.exports导出;
ESM通过import引入,export导出;
普通模块引入
ts
const path = require("path");//CJS
import path from "path";//ESJ
json 文件的引入
ts
const pkg = require("../package"); //CJS 获取package.json的数据
import pkg from "../package.json" assert { type: "json" };//ESJ
import是不可以直接引入json文件的
普通模块导出
ts
module.exports = upgrade;//cjs
export default upgrade;//esj
CJS兼容ESM
ts
(async function() {
const esm = await import('esm');
esm.a();
esm.b();
})();
ESM兼容CJS(推荐)
ESM可以直接 importCJS 模块,越来越多的库使用ESM,建议新项目优先使用ESM规范。
ts
import { a, b } from 'cjs';
a();
b():
//或者
import a from 'cjs';
a()
全局变量--dirname-filename-esm库
ESM中没有注入__dirname、__filename全局变量,可以通过dirname-filename-esm库获取
__dirname
:执行命令的文件所在目录的绝对路径(不包含当前文件名)
__filename
:当前文件的绝对路径(包含文件名)
ts
import { dirname } from 'dirname-filename-esm'
// 拿到执行命令文件所在目录的绝对路径
const __dirname = dirname(import.meta)
console.log(__dirname)
import { filename } from 'dirname-filename-esm'
// 获取文件绝对路径
const __filename = filename(import.meta)
// 如果不引入库dirname-filename-esm 可以通过以下方式
// import { fileURLToPath } from 'node:url'
// const __filename = fileURLToPath(import.meta.url)
问题
Error: EPERM: operation not permitted, rename
请检查当前命令是不是在编辑器中执行,要修改的文件夹是不是在编辑器中被打开
;如果被打开,请关闭编辑器,在cmd等终端的对应项目目录下执行
请检查当前命令执行的命令,是否已经存在将要被修改成的名称的文件夹或文件
,导致了命名冲突的问题;如果已经存在,修改新的命名,或者删除已经存在的命名
SyntaxError: Cannot use import statement outside a module
该问题就是在cjs规范的项目中使用了import的导入方式,此时 :
- package.json 文件中 type:commonjs,表明使用CJS规范
- 报错文件使用js后缀,将默认遵循package.json设定的规范
- 报错文件中使用了ESM的规范
解决方案是,将报错文件的后缀改为mjs,表明当前文件使用ESM规范
Error: Cannot find module 'D:\serverjs\index'
该问题是在package中script中定义的某个命令test运行导致的:
json
{
"script":{
"test" :"node ./serverjs/index"
}
}
运行后报错:
`
node:internal/modules/cjs/loader:936
throw err;
Error: Cannot find module 'D:\serverjs\index'
`
然后找到该文件确实存在,但是后缀名是".mjs",所以命令无法自动识别后缀非js文件,需要在命令中明确后缀名
node ./serverjs/lowdb/bin/index.mjs
问题解决!!!