Commonjs和ECMAScript语法

Commonjs

Nodejs中默认采用Commonjs语法。

模块导出格式如下。

1
2
3
4
//commonjs语法导出自定义模块
module.exports = {
setResHeader: setResHeader
}

模块导入格式如下。

1
2
//引入自定义工具模块,只能使用路径
let resHeaderUtil = require("./utils/resHeaderUtil")

ECMAScript

比如vue这种框架,采用的就是ECMAScript语法。要使用这种语法,首先得在package.json中指出type的值。

When set to “module”, the type field allows a package to specify all .js files within are ES modules. If the “type” field is omitted or set to “commonjs”, all .js files are treated as CommonJS.

1
2
3
{
"type": "module"
}

这便设置成了。

默认导出

1
2
3
4
//ECMAScript语法默认导出,外部import以后,全部加载
export default {
setResHeader: setResHeader
}

当我们需要时,如下导入即可。

1
2
//引入自定义工具模块,需要附带文件的后缀
import resHeaderUtil from './utils/resHeaderUtil-ecmascript-default.js'

命名导出

只需要在变量前面加上 export 修饰,即可在其它地方导入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//设置http响应体的Content-Type
//ECMAScript语法命名导出,外部import按需导入,按需加载

export function setResHeader(response, contentType) {
if (contentType == "html")
response.setHeader("Content-Type", "text/html;charset=utf-8");
else if (contentType == "css")
response.setHeader("Content-Type", "text/css;charset=utf-8");
else if (contentType == "js")
response.setHeader("Content-Type", "application/javascript;charset=utf-8");
else
response.setHeader("Content-Type", "text/plain;charset=utf-8");
}

命名导出方式,在导入时,是按需导入,按需加载。花括号中放置需要的变量,不用像默认导出那样将整个模块都导入进来。

需要注意的是,花括号中的变量名,要和引入模块中的变量名或者函数名相同。

1
2
//引入自定义工具模块,需要附带文件的后缀
import { setResHeader } from './utils/resHeaderUtil-ecmascript-named.js'

语法差异

在Common.js中,我们可以使用__dirname来获取当前js文件所在目录的绝对路径,通过node:path模块的join方法来拼接自己想要的绝对路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//添加监听事件
server.on("request", (req, res) => {
let pathname = req.url
var postfix = pathname.match(/(\.[^.]+|)$/)[0];//取得后缀名

//readFile方法使用相对路径时,查找的起点是执行node xx.js命令的目录。这个目录与我们打开终端的位置有关,所以最好采用绝对路径。
//其中__dirname是当前文件夹的绝对路径,通过path.join方法拼接路径可以将拼接后的路径转换成当前操作系统支持识别的绝对路径
let filePath = path.join(__dirname, "./Login", pathname)
fs.readFile(filePath, (err, data) => {
if (err) {
res.setHeader("Content-Type", "text/html;charset=utf-8")
res.end("<h1>访问的内容不存在</h1>")
} else {
//根据后缀设置响应头
if (postfix == "html")
resHeaderUtil.setResHeader(res, "html")
else if (postfix == "css")
resHeaderUtil.setResHeader(res, "css")
else if (postfix == "js")
resHeaderUtil.setResHeader(res, "js")
//以...内容结束响应
res.end(data.toString())
}
})
})

但是当我们使用ES语法后,__dirname就不能使用了,否则会报错如下。

ReferenceError: __dirname is not defined in ES module scope

但是我们使用fs.readFile方法时,又经常需要使用到绝对路径。作为__dirname的替代方案,可以实现工具类如下。

1
2
3
4
5
6
7
8
9
10
11
import url from 'node:url'
import path from 'node:path'

export function getRecentDir(fileUrl) {
//将文件url转换成文件的绝对路径
let filePath = url.fileURLToPath(fileUrl)
//获取绝对路径的最后目录名
let dirname = path.dirname(filePath)
//返回给外界使用
return dirname
}

外界传入对应的文件URL即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//引入自定义工具模块,需要附带文件的后缀
import { setResHeader } from './utils/resHeaderUtil-ecmascript-named.js'
//导入自定义的文件绝对路径工具
import { getRecentDir } from './utils/recentPath.js'

//添加监听事件
server.on("request", (req, res) => {
let pathname = req.url
var postfix = pathname.match(/(\.[^.]+|)$/)[0];//取得后缀名

//readFile方法使用相对路径时,查找的起点是执行node xx.js命令的目录。这个目录与我们打开终端的位置有关,所以最好采用绝对路径。
//在ES语法中没有__dirname,要通过文件url转换得到当前文件夹的绝对路径,通过path.join方法拼接路径可以将拼接后的路径转换成当前操作系统支持识别的绝对路径
let filePath = path.join(getRecentDir(import.meta.url), "./Login", pathname)
fs.readFile(filePath, (err, data) => {
if (err) {
res.setHeader("Content-Type", "text/html;charset=utf-8")
res.end("<h1>访问的内容不存在</h1>")
} else {
//根据后缀设置响应头
if (postfix == "html")
// resHeaderUtil.setResHeader(res, "html")
setResHeader(res, "html")
else if (postfix == "css")
// resHeaderUtil.setResHeader(res, "css")
setResHeader(res, "css")
else if (postfix == "js")
// resHeaderUtil.setResHeader(res, "js")
setResHeader(res, "js")
//以...内容结束响应
res.end(data.toString())
}
})
})