node.js及其常用模块

Node.js是运行在服务端的JavaScript,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
其特点:单线程,异步非阻塞I/O,事件驱动,速度快

模块

引用模块使用require函数

模块加载机制被称为CommonJS规范。在这个规范下,每个.js文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.jsmain.js都申明了全局变量var s = 'xxx',但互不影响。

exports 向外暴露多个对象

  • 对外暴露对象
    使用exports.[function name] = [function name]
  • 引用暴露的对象
    xxx = require('./a')
    此时的xxx等价于a文件中的exports
1
2
3
// func.js
exports.add = (num1, num2) => num1 + num2;
exports.sub = (num1, num2) => num1 - num2;
1
2
3
4
5
// index.js
var func = require('./func')

console.log(func.add(1,2))
console.log(func.sub(1,2))

module.exports 向外暴露单个对象

  • 一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable;,输出的变量可以是任意对象、函数、数组等等。
  • 一个模块要引用其他模块暴露的变量,用var ref = require('module_name');就拿到了引用模块的变量。
1
2
3
4
5
6
// func.js

function func(name) {
console.log(s + ', ' + name + '!');
}
module.exports = func; // 使用module.exports来对外暴露变量
1
2
3
4
// mian.js
var func = require('./func');

func() // 直接调用

exportsmodule.exports的一个引用,exports指向的是module.exports

node_modules文件夹

node_modules是安装node后用来存放用包管理工具下载安装的包的文件夹.

node是如何找到模块的?

阶段1. 粗查阶段

  • 如果是node核心模块,就直接返回模块名称
  • 如果是引入的第三方npm模块,会返回父级所在文件夹下的node_modules,父父级所在文件夹下的node_modules,依次递归,一直到/node_modules和用户名下的.node_modules以及全局环境变量配置的全局安装的模块文件夹组成的数组
  • 如果是相对路径引入的模块,会将相对路径和父级路径之间进行一个path.resolve(),然后返回

阶段2. 精确查找,获取文件绝对路径

require('express')为例

  • 先尝试加载node_modules/express,这种没有扩展名的文件是否存在
  • 尝试按照扩展名规则查找,依次判断node_modules文件夹下.js .json .node结尾的文件名为express的文件是否存在,返回文件的绝对路径
  • 判断node_modules/express文件夹下的package.json是否存在,如果存在,返回main字段指定的文件的绝对路径
  • 判断node_modules/express/index.js是否存在,存在返回对应文件绝对路径

使用 package.json 管理依赖

一个 package.json 文件可以有以下几点作用:

  • 作为一个描述文件,描述了你的项目依赖哪些包
  • 允许我们使用 “语义化版本规则” 指明你项目依赖包的版本
  • 让你的构建更好地与其他开发者分享,便于重复使用

Http模块

nodejs不需要使用apache、nginx等服务器,nodejs不仅仅实现了一个应用,同时还实现了整个http服务器。

1
2
3
4
5
6
7
var http = require("http");

var app = http.createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"}); // 设置响应头
response.end("hello world"); // 设置响应内容
});
app.listen(80); // 开启服务

http模块的createServer的回调函数中

  • request代表客户端的请求数据,是一个可读流
    request.url 请求的 url 路由
    request.method 请求的方式(GET、POST…)
  • response代表服务端的响应数据,是一个可写流
    response.write(xxx) 往响应中写数据
    response.end(xxx) 结束写返回数据
    response.setHeader(xxx) 设置响应头
    响应中的内容只能是二进制流或字符串

GET/POST请求

请求方式可以通过request.method获取

获取GET请求内容

由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此可以手动解析后面的内容作为GET请求的参数。

1
let request_get = url.parse(request.url, true)['query'];

获取POST请求内容

POST请求的内容全部的都在请求体中,http.ServerRequest并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。

比如上传文件,而很多时候我们可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所有node.js默认是不会解析请求体的, 当你需要的时候,需要手动来做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var http = require('http');
var url = require('url');
var querystring = require('querystring');

http.createServer(function(request, response){
var post = ''; //定义了一个post变量,用于暂存请求体的信息

request.on('data', (chunk) => { post += chunk;}); //通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中

request.on('end', () => { //在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
post = querystring.parse(post);
for (key in post) {
response.write(key + '=' + post[key] + '<br>');
}
response.end("");
});
}).listen(80);

url模块

url.parse()

解析一个url地址,返回一个url对象
url.parse(url, bool parseQueryString = false, bool slashesDenoteHost = false)

  • url: url地址字符串
  • parseQueryString: 若为true,返回的url对象中query属性返回的是一个对象
  • slashesDenoteHost: 若为true,则//之后至下一个/之前的字符串会解析作为host.
    例如,//foo/bar会解析为{host:'foo',pathname:'/bar'} 而不是 {pathname:'//foo/bar'}.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
console.log(url.parse('http://127.0.0.1:5000/test/test?id=1&name=a#123'))

结果为:
Url {
protocol: 'http:',
slashes: true,
auth: null,
host: '127.0.0.1:5000',
port: '5000',
hostname: '127.0.0.1',
hash: '#123',
search: '?id=1&name=a',
query: 'id=1&name=a',
pathname: '/test/test',
path: '/test/test?id=1&name=a',
href: 'http://127.0.0.1:5000/test/test?id=1&name=a#123'
}

若第二个参数为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
console.log(url.parse('http://127.0.0.1:5000/test/test?id=1&name=a#123', true))

Url {
protocol: 'http:',
slashes: true,
auth: null,
host: '127.0.0.1:5000',
port: '5000',
hostname: '127.0.0.1',
hash: '#123',
search: '?id=1&name=a',
query: [Object: null prototype] { id: '1', name: 'a' },
pathname: '/test/test',
path: '/test/test?id=1&name=a',
href: 'http://127.0.0.1:5000/test/test?id=1&name=a#123'
}

url.format()

将一个url对象转化成url字符串

url.format(urlObject)
其中的urlObject是一个url对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let urlobj = {
protocol: 'http',
hostname: '127.0.0.1',
port: '5000',
pathname: '/login',
query: {
id: 1,
user: 'admin',
pass: 'pass'
}
}
console.log(url.format(urlobj))

结果: http://127.0.0.1:5000/login?id=1&user=admin&pass=pass

path模块

path 模块提供了一些用于处理文件路径的小工具

path.parse()

解析并返回路径字符串的对象。

path.parse(pathString)

1
2
3
4
5
6
7
8
9
10
11
let file_path = '/var/www/html/index.html'
console.log(path.parse(file_path))

结果:
{
root: '/',
dir: '/var/www/html',
base: 'index.html',
ext: '.html',
name: 'index'
}

path.dirname()

返回路径中代表文件夹的部分

1
2
3
path.dirname('/var/www/html/index.html');

==> /var/www/html

path.basename(p[, ext])

返回路径中的最后一部分(文件名)

1
2
3
path.basename('/var/www/html/index.html');

==> index.html

path.dirname(p)

返回路径中代表文件夹的部分

1
2
3
path.basename('/var/www/html/index.html');

==> /var/www/html

path.extname(p)

返回路径中文件的后缀名,即路径中最后一个.之后的部分。
如果一个路径中并不包含.或该路径只包含一个. 且这个.为路径的第一个字符,则此命令返回空字符串。

1
2
3
path.extname('/var/www/html/index.html');

==> .html

fs模块

Node.js内置的fs模块就是文件系统模块,负责读写文件。(file system)

所有与文件操作都是通过fs核心模块来实现的,包括文件目录的创建、删除、查询以及文件的读取和写入,在 fs 模块中,所有的方法都分为同步和异步两种实现,具有 sync 后缀的方法为同步方法,不具有 sync 后缀的方法为异步方法,在了解文件操作的方法之前有一些关于系统和文件的前置知识,如文件的权限位 mode、标识位 flag、文件描述符 fd

标识位 flag

NodeJS 中,标识位代表着对文件的操作方式,如可读、可写、即可读又可写等等

符号 含义
r 读取文件,如果文件不存在则抛出异常。
r+ 读取并写入文件,如果文件不存在则抛出异常。
rs 读取并写入文件,指示操作系统绕开本地文件系统缓存。
w 写入文件,文件不存在会被创建,存在则清空后写入。
wx 写入文件,排它方式打开。
w+ 读取并写入文件,文件不存在则创建文件,存在则清空后写入。
wx+ w+ 类似,排他方式打开。
a 追加写入,文件不存在则创建文件。
ax a 类似,排他方式打开。
a+ 读取并追加写入,不存在则创建。
ax+ a+ 类似,排他方式打开。

文件读取

同步读取方法(readFileSync)

readFileSync 有两个参数:

  • 第一个参数为读取文件的路径
  • 第二个参数为 options,默认值为 null,其中有 encoding(编码,默认为 null)和 flag(标识位,默认为 r),也可直接传入 encoding
  • 返回值为文件的内容,如果没有 encoding,返回的文件内容为 Buffer,如果有按照传入的编码解析。
1
2
3
4
5
var fs = requere('fs');

let data = fs.readFileSyc('a.txt', 'utf8');
// let data = fs.readFileSync('index.js', {encoding: 'utf8', flag: 'r+'})
console.log(data);

异步读取文件(readFile)

异步读取方法 readFilereadFileSync 的前两个参数相同,最后一个参数为回调函数,函数内有两个参数 err(错误)和 data(数据)

  • 当正常读取时,err参数为nulldata参数为读取到的String
  • 当读取发生错误时,err参数代表一个错误对象,dataundefined

这也是Node.js标准的回调函数:第一个参数代表错误信息,第二个参数代表结果

1
2
3
4
5
6
7
8
9
10
11
'use strict';

var fs = require('fs');

fs.readFile('a.txt', 'utf-8', function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});

文件写入

同步写入(writeFileSync)

writeFileSync 有三个参数:

  • 第一个参数为写入文件的路径或文件描述符;
  • 第二个参数为写入的数据,类型为 StringBuffer
  • 第三个参数为 options,默认值为 null,其中有 encoding(编码,默认为 utf8)、 flag(标识位,默认为 w)和 mode(权限位,默认为 0o666),也可直接传入 encoding
1
fs.writeFileSync('a.txt','hello world');

异步写入(writeFile)

异步写入方法 writeFilewriteFileSync 的前三个参数相同,最后一个参数为回调函数,函数内有一个参数 err(错误),回调函数在文件写入数据成功后执行。

1
2
3
4
5
6
7
fs.writeFile("2.txt", "Hello world", err => {
if (!err) {
fs.readFile("2.txt", "utf8", (err, data) => {
console.log(data); // Hello world
});
}
});
文章作者: Dar1in9
文章链接: http://dar1in9s.github.io/2020/05/19/node/nodejs的常用模块/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dar1in9's Blog