Hello World
# Hello World
[TOC]
# 一、认识Node.js
- Node.js采用C++语言编写而成,是一个JavaScript的运行环境。
- Node.js采用了谷歌浏览器的V8引擎,性能很好,同时还提供了很多系统级的API,如文件操作、网络编程等。
- Node.js采用事件驱动、异步编程,为网络服务而生。
- 充分利用了系统资源,执行代码无须阻塞等待某种操作,有限的资源可以用于其他任务。
- Node.js采用单进程、单线程模式。
- Node.js支持的编程语言是JavaScript。
- JavaScript的匿名函数和闭包特性非常适合事件驱动、异步编程。
- JavaScript在动态语言中性能较好。
# 二、第一个服务端应用
# 2.1 Hello World
短短几行代码,就可以构建服务器。
// $ node app.js
// 导入node自带的http模块
const http = require('http');
// 默认就是127.0.0.1
const hostname = '127.0.0.1';
const port = 3000;
// 开启http服务
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
// 在客户端写些东西
res.write('write something\n')
// 将内容返回到客户端,显式地结束请求
res.end('Hello World\n');
});
// 监听本地3000端口
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2.2 一个规范的接口
- 接口名称:要体现出大致的使用场景,例如增删改查的动作。
- 接口返回:要有规范化的标识,如成功与否、错误内容等。
# 2.3 获取命令行传来的参数
const arguments = process.argv.splice(2)
1
process.argv[0] : 返回启动Node.js进程的可执行文件所在的绝对路径。
process.argv[1] : 为当前执行的JavaScript文件路径。
process.argv.splice(2) : 移除前两者后,剩余的元素为其他命令行参数(也就是我们自定义部分)。
# 三、调试
# 3.1 Debugger
# 3.2 Node InSpector
可以像调试浏览器中的JavaScript代码一样调试Node中的JavaScript代码。
$ npm install -g node-inspector
1
# 四、文件系统
# 4.1 初识文件系统
- fs模块主要用于文件的读写、移动、复制、删除、重命名等操作。
- 同步:rename;异步:renameSync。
const fs = require('fs');
// 异步写入文件(path, data, cb),默认覆盖旧文件内容
fs.writeFile('./a.txt', 'write something', (err) => {
err&&console.log(err);
})
// 异步读取文件
fs.readFile('./a.txt', (err,doc) => {
if(err) {
return console.error(err);
}
// 不加toString,则输出Buffer对象
// <Buffer 77 72 69 74 65 20 73 6f 6d 65 74 68 69 6e 67>
console.log(doc.toString());
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4.2 req.url
- 得到/开头的url
const http=require('http');
const fs=require('fs');
let server=http.createServer(function (req, res){
// www文件夹存放写给浏览器的文件
fs.readFile(`www${req.url}`, (err, buffer)=>{
if(err){
// 写给机器的
res.writeHeader(404);
// 写给人的
res.write('Not Found');
res.end();
}else{
res.write(buffer);
res.end();
}
});
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 4.3 文件路径
var path = required('path')
console.log(__dirname) // 总是返回被执行的js所在文件夹的绝对路径
console.log(__filename) // 总是返回被执行的js的绝对路径
console.log(process.cwd()) // 总是返回运行 node 命令时所在的文件夹的绝对路径
console.log(path.resolve('./')) // 总是返回运行 node 命令时所在的文件夹的绝对路径
1
2
3
4
5
6
2
3
4
5
6
# 4.3.1 path API
path.dirname()
: 返回 path 的目录名。path.join()
:所有给定的 path 片段连接到一起,然后规范化生成的路径。path.resolve()
:方法会将路径或路径片段的序列解析为绝对路径,解析为相对于当前目录的绝对路径,相当于cd命令。 resolve把/
当成根目录。
# 五、接收浏览器的数据
# 5.1 表单GET提交
<!-- form_get.html -->
<form action="http://localhost:8080/aaa" method="get">
用户:<input type="text" name="username" /><br>
密码:<input type="password" name="password" /><br>
<input type="submit" value="提交" />
</form>
<!-- 提交后 -> http://localhost:8080/aaa?username=123&password=aaa -->
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
// server_get1.js
const http=require('http');
const querystring=require('querystring');
let server=http.createServer(function (req, res){
// 在?处切一刀
let [url, query]=req.url.split('?');
// 解析url参数
let get=querystring.parse(query);
console.log(url, get);
// /aaa [Object: null prototype] { username: '123', password: 'aaa' }
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
// server_get2.js
const http=require('http');
const url=require('url');
let server=http.createServer(function (req, res){
// 加true是为将query由字符串转换成对象形式
let {pathname, query}=url.parse(req.url, true);
console.log(pathname, query);
// 无true时:/aaa username=123&password=aaa
// /aaa [Object: null prototype] { username: '123', password: 'aaa' }
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 5.2 表单POST提交
# 5.2.1 对普通数据的处理
// server_post.js
const http=require('http');
const querystring=require('querystring');
let server=http.createServer(function (req, res){
let arr=[];
req.on('data', buffer=>{
arr.push(buffer);
});
// 遇到end表示传完了
req.on('end', ()=>{
let buffer=Buffer.concat(arr);
// 一般情况下不可以toString
// 比如传输的数据是个视频而不是纯粹的字符串
let post=querystring.parse(buffer.toString());
console.log(post);
// [Object: null prototype] { username: '123', password: 'aaa' }
});
});
server.listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 5.2.2 对文件数据的处理
自己写原生的话,要对http协议的规范要很清楚,还要懂得对buffer二进制数据的处理。
这里可以借助一个multiparty包(要自己先安装)。
// file_post.html
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
用户:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="file" name="f1">
<input type="submit" value="提交">
</form>
1
2
3
4
5
6
7
2
3
4
5
6
7
// server.js
const http=require('http');
const multiparty=require('multiparty');
http.createServer((req, res)=>{
let form=new multiparty.Form({
uploadDir: './upload'
});
form.parse(req);
form.on('field', (name, value)=>{
console.log('字段:', name, value);
});
form.on('file', (name, file)=>{
console.log('文件:', name, file);
});
form.on('close', ()=>{
console.log('表单解析完成');
});
}).listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5.3 GET和POST的整合
// server_total.js
const http=require('http');
const url=require('url');
const querystring=require('querystring');
const fs=require('fs');
http.createServer((req, res)=>{
let path='', get={}, post={};
if(req.method=='GET'){
let {pathname, query}=url.parse(req.url, true);
path=pathname;
get=query;
complete();
}else if(req.method=='POST'){
path=req.url;
let arr=[];
req.on('data', buffer=>{
arr.push(buffer);
});
req.on('end', ()=>{
let buffer=Buffer.concat(arr);
post=querystring.parse(buffer.toString());
complete();
});
}
function complete(){
console.log(path, get, post);
}
}).listen(8080);
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
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
# 5.3.1 应用-注册登录
<head>
<meta charset="utf-8">
<title></title>
<script src="jquery.js" charset="utf-8"></script>
<script>
$(function (){
$('#btn1').click(()=>{
$.ajax({
url: '/reg',
data: {
username: $('#user').val(),
password: $('#pass').val()
},
dataType: 'json'
}).then(json=>{
if(json.error){
alert(json.msg);
}else{
alert('注册成功');
}
}, err=>{
alert('注册失败,请刷新重试');
});
});
$('#btn2').click(()=>{
$.ajax({
url: '/login',
data: {
username: $('#user').val(),
password: $('#pass').val()
},
dataType: 'json'
}).then(json=>{
if(json.error){
alert(json.msg);
}else{
alert('登录成功');
}
}, err=>{
alert('登录失败,请刷新重试');
});
});
});
</script>
</head>
<body>
用户:<input type="text" id="user" /><br>
密码:<input type="password" id="pass" /><br>
<input type="button" value="注册" id="btn1">
<input type="button" value="登录" id="btn2">
</body>
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
43
44
45
46
47
48
49
50
51
52
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
43
44
45
46
47
48
49
50
51
52
// server.js
const http=require('http');
const url=require('url');
const querystring=require('querystring');
const fs=require('fs');
let users={};
http.createServer((req, res)=>{
let path='', get={}, post={};
if(req.method=='GET'){
//...
}else if(req.method=='POST'){
//...
}
function complete(){
if(path=='/register'){
let {username, password}=get;
if(users[username]){
res.write(JSON.stringify({error: 1, msg: '此用户名已存在'}));
res.end();
}else{
users[username]=password;
res.write(JSON.stringify({error: 0, msg: ''}));
res.end();
}
}else if(path=='/login'){
let {username, password}=get;
if(!users[username]){
res.write(JSON.stringify({error: 1, msg: '找不到此用户'}));
res.end();
}else if(users[username]!=password){
res.write(JSON.stringify({error: 1, msg: '密码不对'}));
res.end();
}else{
res.write(JSON.stringify({error: 0, msg: ''}));
res.end();
}
}else{
fs.readFile(`www${path}`, (err, buffer)=>{
if(err){
res.writeHeader(404);
res.write('Not Found');
res.end();
}else{
res.write(buffer);
res.end();
}
});
}
}
}).listen(8080);
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 六、其他
# 6.1 零碎知识
# 6.1.1 url模块
url.parse
:可以将一个url的字符串解析并返回一个url的对象。
const url = require('url')
url.parse("http://baidu.com:8080/test/h?query=js#node")
Url {
protocol: 'http:',
slashes: true,
auth: null,
host: 'baidu.com:8080',
port: '8080',
hostname: 'baidu.com',
hash: '#node',
search: '?query=js',
query: 'query=js',
pathname: '/test/h',
path: '/test/h?query=js',
href: 'http://baidu.com:8080/test/h?query=js#node'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
url.format
:将传入的url对象编程一个url字符串并返回。
url.format({
protocol: 'https',
hostname: 'example.com',
pathname: '/some/path',
query: {
page: 1,
format: 'json'
}
});
// => 'https://example.com/some/path?page=1&format=json'
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11