NodeJs学习

Node.JS

Node.JS是使用javascript来开发后端的工具。

可以使得JS代码不再依托浏览器运行。

(注:Node.js也可称为Node,NodeJS)

第一个Node程序

1
2
3
//test.js
'use strict'
console.log('hello world!')
1
2
3
//终端
//运行test.js
node test.js

在终端可输入node进入Node交互模式,交互模式下可输入JS代码直接运行,并且node会将代码结果自行打印出来

使用严格模式

如果在JavaScript文件开头写上'use strict';,那么Node在执行该JavaScript时将使用严格模式。但是,在服务器环境下,如果有很多JavaScript文件,每个文件都写上'use strict';很麻烦。我们可以给Nodejs传递一个参数,让Node直接为所有js文件开启严格模式:

1
node --use_strict calc.js

模块

模块化最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Node内置的模块和来自第三方的模块。

使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。

例如:

  • 导出
1
2
3
4
5
6
7
8
9
10
//hello.js
'use strict'

let s = 'Hello';

greet(name){
console.log(s + ' ' + name + '!');
}

module.exports = greet;
1
2
//也可以直接 exports 但更推荐上面的方式
exports = greet
  • 导入
1
2
3
//main.js
//导入刚刚的hello模块
let greet = require('./hello')

global

avaScript有且仅有一个全局对象,在浏览器中,叫window对象。而在Node.js环境中,也有唯一的全局对象,但不叫window,而叫global,这个对象的属性和方法也和浏览器环境的window不同。

在Node交互环境中输入

1
2
3
4
5
6
7
8
9
10
11
12
> global.console
Console {
log: [Function: bound ],
info: [Function: bound ],
warn: [Function: bound ],
error: [Function: bound ],
dir: [Function: bound ],
time: [Function: bound ],
timeEnd: [Function: bound ],
trace: [Function: bound trace],
assert: [Function: bound ],
Console: [Function: Console] }`

process

process也是Node.js提供的一个对象,它代表当前Node.js进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> process === global.process;
true
> process.version;
'v5.2.0'
> process.platform;
'darwin'
> process.arch;
'x64'
> process.cwd(); //返回当前工作目录
'/Users/michael'
> process.chdir('/private/tmp'); // 切换当前工作目录
undefined
> process.cwd();
'/private/tmp'

process.nextTick()

process.nextTick()接收一个函数。

传入process.nextTick()的函数不是立刻执行,而是要等到下一次事件循环。

即循环执行代码一次,等到第二次循环再执行process.nextTick()的内容

例如:

1
2
3
4
5
6
7
8
9
10
11
12
// test.js

// process.nextTick()将在下一轮事件循环中调用:
let nextTick1 = process.nextTick(() => {
console.log('next tick callback')
})

console.log('next was set')
let nextTick2 = process.nextTick(() => {
console.log('next tick callback')
})
console.log('next was set twice')
1
2
3
4
5
//输出
next was set
next was set twice
next tick callback
next tick callback

执行过程:

  • 第一次循环代码
    • 跳过nextTick1
    • 执行console.log('next was set')
    • 跳过nextTick2
    • 执行console.log('next was set twice')
  • 第二次循环代码
    • 执行nextTick1
    • 跳过console.log('next was set')
    • 执行nextTick2
    • 跳过console.log('next was set twice')

exit事件

Node.js进程本身的事件就由process对象来处理。如果我们响应exit事件,就可以在程序即将退出时执行某个回调函数

例如:

1
2
3
4
5
// test.js
// 程序即将退出时的回调函数:
process.on('exit', function (code) {
console.log('about to exit with code: ' + code);
});

判别JS执行环境

有很多JavaScript代码既能在浏览器中执行,也能在Node环境执行,但有些时候,程序本身需要判断自己到底是在什么环境下执行的,常用的方式就是根据浏览器和Node环境提供的全局变量名称来判断:

1
2
3
4
5
6
7
// test.js
//通过独特的全局对象来判别
if (typeof(window) === 'undefined') {
console.log('node.js');
} else {
console.log('browser');
}

基本模块

fs

用于读写文件

异步读取文本

例如:

1
2
3
4
5
6
7
8
9
10
11
12
// test.js
'use strict';

var fs = require('fs');

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

回调函数需要两个参数:err,data

  • 正常情况下err == null , data为读取到的文本

  • 发生错误时err为一个错误对象,data == undefined

读取二进制文件(异步)

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// test.js
// 读取图片
'use strict';

var fs = require('fs');

//注意这里没有传入文件编码格式
fs.readFile('sample.png', function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
console.log(data.length + ' bytes');
}
});

当读取二进制文件时,不传入文件编码时,回调函数的data参数将返回一个Buffer对象。在Node.js中,Buffer对象就是一个包含零个或任意个字节的数组(注意和Array不同)。

Buffer对象可以和String作转换,例如,把一个Buffer对象转换成String:

1
2
3
4
// Buffer -> String
// text为Buffer
var text = data.toString('utf-8');
console.log(text);

同步读取文件

除了标准的异步读取模式外,fs也提供相应的同步读取函数。同步读取的函数和异步函数相比,多了一个Sync后缀,并且不接收回调函数,函数直接返回结果。

例如:

1
2
3
4
5
6
7
8
9
// test.js
// 同步读取

'use strict';

var fs = require('fs');

var data = fs.readFileSync('sample.txt', 'utf-8');
console.log(data);

如果同步读取发生错误,则需要用try...catch捕获

1
2
3
4
5
6
7
// 异常处理
try {
var data = fs.readFileSync('sample.txt', 'utf-8');
console.log(data);
} catch (err) {
// 出错了
}

异步写文件

将数据写入文件是通过fs.writeFile()实现的

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// test.js
'use strict';

var fs = require('fs');

var data = 'Hello, Node.js';
fs.writeFile('output.txt', data, function (err) {
if (err) {
console.log(err);
} else {
console.log('ok.');
}
});

writeFile()的参数依次为文件名、数据和回调函数。如果传入的数据是String,默认按UTF-8编码写入文本文件,如果传入的参数是Buffer,则写入的是二进制文件。回调函数由于只关心成功与否,因此只需要一个err参数。

同步写文件

1
2
3
4
5
6
7
// test.js
'use strict';

var fs = require('fs');

var data = 'Hello, Node.js';
fs.writeFileSync('output.txt', data);

同样需要使用try...catch捕获异常

获取文件信息(异步)

如果我们要获取文件大小,创建时间等信息,可以使用fs.stat(),它返回一个Stat对象,能告诉我们文件或目录的详细信息

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'use strict';

var fs = require('fs');

fs.stat('sample.txt', function (err, stat) {
if (err) {
console.log(err);
} else {
// 是否是文件:
console.log('isFile: ' + stat.isFile());
// 是否是目录:
console.log('isDirectory: ' + stat.isDirectory());
if (stat.isFile()) {
// 文件大小:
console.log('size: ' + stat.size);
// 创建时间, Date对象:
console.log('birth time: ' + stat.birthtime);
// 修改时间, Date对象:
console.log('modified time: ' + stat.mtime);
}
}
});

获取文件信息(同步)

stat()也有一个对应的同步函数statSync(),请试着改写上述异步代码为同步代码。

stream

流是一种抽象的数据结构。想象水流,当在水管中流动时,就可以从某个地方(例如自来水厂)源源不断地到达另一个地方(比如你家的洗手池)。我们也可以把数据看成是数据流,比如你敲键盘的时候,就可以把每个字符依次连起来,看成字符流。这个流是从键盘输入到应用程序,实际上它还对应着一个名字:标准输入流(stdin)。

如果应用程序把字符一个一个输出到显示器上,这也可以看成是一个流,这个流也有名字:标准输出流(stdout)。流的特点是数据是有序的,而且必须依次读取,或者依次写入,不能像Array那样随机定位

使用流读取文件

​ 一般步骤:创建一个文件流对象,响应data事件从文件流中读出数据,响应end事件关闭流

​ 流也是一个对象,我们只需要响应流的事件就可以了:data事件表示流的数据已经可以读取了,end事件表示这个流已经到末尾了,没有数据可以读取了,error事件表示出错了。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// test.js
'use strict';

var fs = require('fs');

// 打开一个流:
var rs = fs.createReadStream('sample.txt', 'utf-8');

rs.on('data', function (chunk) {
console.log('DATA:')
console.log(chunk);
});

rs.on('end', function () {
console.log('END');
});

rs.on('error', function (err) {
console.log('ERROR: ' + err);
});

要注意,data事件可能会有多次,每次传递的chunk是流的一部分数据。

要以流的形式写入文件,只需要不断调用write()方法,最后以end()结束:

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// test.js
'use strict';

var fs = require('fs');

var ws1 = fs.createWriteStream('output1.txt', 'utf-8');
ws1.write('使用Stream写入文本数据...\n');
ws1.write('END.');
ws1.end();

var ws2 = fs.createWriteStream('output2.txt');
ws2.write(new Buffer('使用Stream写入二进制数据...\n', 'utf-8'));
ws2.write(new Buffer('END.', 'utf-8'));
ws2.end();

pipe

就像可以把两个水管串成一个更长的水管一样,两个流也可以串起来。一个Readable流和一个Writable流串起来后,所有的数据自动从Readable流进入Writable流,这种操作叫pipe

例如:

1
2
3
4
5
6
7
8
9
// test.js
'use strict';

var fs = require('fs');

var rs = fs.createReadStream('sample.txt');
var ws = fs.createWriteStream('copied.txt');

rs.pipe(ws);

注:当Readable流的数据读取完毕,end事件触发后,将自动关闭Writable流。如果我们不希望自动关闭Writable流,需要传入参数:

1
readable.pipe(writable, { end: false });

http

要开发HTTP服务器程序,从头处理TCP连接,解析HTTP是不现实的。这些工作实际上已经由Node.js自带的http模块完成了。应用程序并不直接和HTTP协议打交道,而是操作http模块提供的requestresponse对象。

request对象封装了HTTP请求,我们调用request对象的属性和方法就可以拿到所有HTTP请求的信息;

response对象封装了HTTP响应,我们操作response对象的方法,就可以把HTTP响应返回给浏览器。

简单的服务器

例如:

​ 用Node.js实现一个HTTP服务器程序非常简单。我们来实现一个最简单的Web程序hello.js,它对于所有请求,都返回Hello world!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// server.js
'use strict';

// 导入http模块:
var http = require('http');

// 创建http server,并传入回调函数:
var server = http.createServer(function (request, response) {
// 回调函数接收request和response对象,
// 获得HTTP请求的method和url:
console.log(request.method + ': ' + request.url);
// 将HTTP响应200写入response, 同时设置Content-Type: text/html:
response.writeHead(200, {'Content-Type': 'text/html'});
// 将HTTP响应的HTML内容写入response:
response.end('<h1>Hello world!</h1>');
});

// 让服务器监听8080端口:
server.listen(8080);

console.log('Server is running at http://127.0.0.1:8080/');

文件服务器

扩展一下上面的Web程序。我们可以设定一个目录,然后让Web程序变成一个文件服务器。要实现这一点,我们只需要解析request.url中的路径,然后在本地找到对应的文件,把文件内容发送出去就可以了。

url解析

解析URL需要用到Node.js提供的url模块,它使用起来非常简单,通过parse()将一个字符串解析为一个Url对象:

例如:

1
2
3
4
5
6
// server.js
'use strict';

var url = require('url');

console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash'));

结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
Url {
protocol: 'http:',
slashes: true,
auth: 'user:pass',
host: 'host.com:8080',
port: '8080',
hostname: 'host.com',
hash: '#hash',
search: '?query=string',
query: 'query=string',
pathname: '/path/to/file',
path: '/path/to/file?query=string',
href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' }
path模块

处理本地文件目录需要使用Node.js提供的path模块,它可以方便地构造目录:

例如:

1
2
3
4
5
6
7
8
9
10
11
// server.js
'use strict';

var path = require('path');

// 解析当前目录:
var workDir = path.resolve('.'); // '/Users/michael'

// 组合完整的文件路径:当前目录+'pub'+'index.html':
var filePath = path.join(workDir, 'pub', 'index.html');
// '/Users/michael/pub/index.html'
实现
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
34
35
36
37
38
39
40
41
42
// server.js
'use strict';

var
fs = require('fs'),
url = require('url'),
path = require('path'),
http = require('http');

// 从命令行参数获取root目录,默认是当前目录:
var root = path.resolve(process.argv[2] || '.');

console.log('Static root dir: ' + root);

// 创建服务器:
var server = http.createServer(function (request, response) {
// 获得URL的path,类似 '/css/bootstrap.css':
var pathname = url.parse(request.url).pathname;
// 获得对应的本地文件路径,类似 '/srv/www/css/bootstrap.css':
var filepath = path.join(root, pathname);
// 获取文件状态:
fs.stat(filepath, function (err, stats) {
if (!err && stats.isFile()) {
// 没有出错并且文件存在:
console.log('200 ' + request.url);
// 发送200响应:
response.writeHead(200);
// 将文件流导向response:
fs.createReadStream(filepath).pipe(response);
} else {
// 出错了或者文件不存在:
console.log('404 ' + request.url);
// 发送404响应:
response.writeHead(404);
response.end('404 Not Found');
}
});
});

server.listen(8080);

console.log('Server is running at http://127.0.0.1:8080/');

crypto

crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。

MD5和SHA1

MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示:

1
2
3
4
5
6
7
8
9
const crypto = require('crypto');

const hash = crypto.createHash('md5');

// 可任意多次调用update():
hash.update('Hello, world!');
hash.update('Hello, nodejs!');

console.log(hash.digest('hex')); // 7e1977739c748beac0c0fd14fd26a544

update()方法默认字符串编码为UTF-8,也可以传入Buffer。

如果要计算SHA1,只需要把'md5'改成'sha1',就可以得到SHA1的结果1f32b9c9932c02227819a4151feed43e131aca40

还可以使用更安全的sha256sha512

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 1970-2020 John Doe
  • Visitors: | Views:

请立刻给我钱

支付宝
微信