在 ES 模块的代码中,无法直接使用 require
、__dirname
和 __filename
,否则会报错。
在 CommonJS 模块中:
-
require
用于加载模块 -
__dirname
,CommonJS 模块的内置变量,表示当前模块的目录名。 -
__filename
,CommonJS 模块的内置变量,表示当前模块的文件名。
接下来我们看看,如何在 ES 模块中使用 require
方法和拿到 __dirname
、__filename
require
在 ES 模块化的代码中,直接使用 require
方法加载 CommonJS 模块化的代码会报错:
ReferenceError: require is not defined in ES module scope
例如这个例子,此例子的目录结构如下:
common.cjs
文件为采用 CommonJS 模块化的代码
// common.cjs
function arrConcat(a, b) {
return a.concat(b)
}
module.exports = {
arrConcat
}
my-util.js
、index.js
为采用 ES 模块化的代码
// my-util.js
const isArr = (list) => {
return Array.isArray(list)
}
export {
isArr
}
// index.js
import { isArr } from './my-util.js'
console.log('isArr ', isArr([1, 2, 3]))
const cmm = require('./common.cjs')
const cRes = cmm.arrConcat([1, 2], ['a', 'b'])
console.log('cRes ', cRes)
👆 在 index.js
文件中,直接使用 require
引用 CommonJS 模块的代码。
package.json
文件则告知当前是 ES 模块化的环境:
{
"type": "module"
}
然后在命令行终端使用 node 运行 index.js
文件,就发现报错了:
这个问题可以借助 Node.js 的 createRequire
方法解决。接着看下面的例子,此例子的目录结构与上面的例子一致,唯一的不同是 index.js
文件中使用 createRequire
方法创建 require
方法,并使用此 require
方法加载 common.cjs
文件:
// index.js
import { isArr } from './my-util.js'
import { createRequire } from 'module'
console.log('isArr ', isArr([1, 2, 3]))
const require = createRequire(import.meta.url)
const cmm = require('./common.cjs')
const cRes = cmm.arrConcat([1, 2], ['a', 'b'])
console.log('cRes ', cRes)
然后在终端使用 node 运行 index.js
,可以发现代码正常运行,没有报错了:
createRequire
方法接收一个文件 URL 对象、文件 URL 字符串或绝对路径字符串,返回 require
方法,可以使用该 require
方法在 ES 模块中加载 CommonJS 模块。
import.meta
是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的 URL 。
ESM 中引入 json
ES 模块不支持把 json 文件当成模块引用,而在 CommonJS 模块却可以。
在 ES 模块中,引入 json 文件会报错:
// index.js
import pkg from './package.json'
console.log('pkg ', pkg)
package.json
文件如下:
{
"type": "module"
}
然后在命令行终端运行 index.js
文件,报错如下:
要在 ES 模块中引入 json 文件,也可借助 createRequire
方法实现,通过createRequire
方法创建 require
方法,再使用该 require
方法引入 json 文件:
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const pkg = require('./package.json')
console.log('pkg ', pkg)
__dirname 和 __filename
在 CommonJS 中可以正常使用 __dirname
和 __filename
:
// index.js
console.log('__dirname ', __dirname)
console.log('__filename ', __filename)
在命令终端使用 node 运行 index.js
文件:
而如果是在 ES 模块环境中,运行上面的代码,则会报错:
在 package.json
中指定 type
为 module
,表示当前为 ES 模块化环境:
{
"type": "module"
}
index.js
文件的内容同上面 CommonJS 中的一样,然后在命令终端使用 node 运行 index.js
文件,发现报错了:
在 ES 模块中,可以使用 import.meta.url
,拿到类似 __filename
的值:
console.log(import.meta.url)
执行后,会得到如下结果
file:///D:/work/code/demo/module-demo6/index.js
然后可通过 url
模块的 fileURLToPath
方法将 import.meta.url
转换为 __filename
的值
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
console.log('__filename ', __filename)
// __filename D:\work\code\demo\module-demo6\index.js
url.fileURLToPath(url)
方法用于将文件 URL 转换为文件系统路径。
有了 __filename
,就可通过 path
模块的 dirname
方法获取到 __dirname
的值了
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
console.log('__dirname ', __dirname)
// __dirname D:\work\code\demo\module-demo6
path.dirname(path)
方法用于获取指定路径的目录名部分。它返回的是去掉文件名后剩下的路径字符串。
总结
在 ES 模块化环境中,由于直接使用 require
会报错,可以通过 Node.js 的 createRequire
方法创建兼容的 require
函数,以此来加载 CommonJS 模块或引入 JSON 文件。
同时,在 ES 模块中,可借助 import.meta.url
来获得在 CommonJS 模块中才可使用的 __dirname
和 __filename
内置变量。