根据B站黑马记录的笔记,并且也做了对应的思维导图,搭配食用更佳

Node.JS简介

何为Node.JS

  • Node.JS为JavaScript运行时
  • Node.JS是JavaScript的运行平台
  • 它既不是一门语言,也不是框架,它是一个平台

Node.JS中的JS

  • Node.JS没有dom和bom
  • EcmaScript
  • Node.JS在JavaScript执行环境中为JavaScript提供一些服务器级别的API
    • 文件读写
    • 网络服务构建
    • 网络通信
    • http服务器
    • ……

Node.JS构建于Chrome的V8引擎

  • 代码只是具有特定格式的字符串
  • 引擎可以认识它,帮你解析和执行
  • Google Chrome的V8引擎是目前公认的解析执行JavaScript代码最快的
  • Node.js的作者把Google Chrome中的V8引擎移植出来,开发了一个独立的JavaScript运行时环境

Node.JS的特性

  • envent-driven 事件驱动
  • non-blocking I/O mode 非阻塞I/O模型(异步)
  • ightweight and efficent. 轻量和高效

Node.JS的作用

  • web服务器后台

  • 命令行工具
    *npm(node)

    • git(c语言)
    • hexo(node)

其它

npm为世界上最大的开源生态系统

初识

安装配置

  • 下载:https://nodejs.org/en/
  • 安装node环境
  • 检测node环境
    *开启命令行提示符(cmd)
    *输入node --vesion
    *或node -v
    *正常状态下返回版本号
  • 解析执行JavaScript
    *创建和编写JavaScript脚本文件
    *打开终端(cmd),定位脚本文件所在位置(也就是执行脚本所在文件目录)
    *输入node 文件名进行执行
    注意:文件名不要使用Node.JS命名,并且最好不用中文

fs

文件操作模块

  • 浏览器中的 JavaScript 没有文件操作能力
  • Node中的 JavaScript 具有文件操作能力
  • fs 是 file-system 的简写,为文件系统的意思
  • 在 Node 中如果想要进行文件操作,就必须引入 fs 这个核心模块
  • 在 fs 这个核心模块中,就提供了所有文件操作相关的 API,
    • fs.readFile 用来读取文件
    • fs.writeFile用来写入文件
  • 文件中存储的都是二进制数据,用toString()方法转换为字符串

读取文件

1.使用require方法加载fs核心模块

2.使用readFile函数读取文件

  • 两个参数,第一个为文件路径,第二个为回调函数

  • 回调函数中有两个参数

  • 第一个参数为返回错误,第二个为数据

  • 成功:error: null; data: 返回数据

  • 失败: error: 错误对象; data: undefined

  • 可以利用error进行语句判断

1
2
3
4
5
6
7
8
9
10
11
12
//使用node中的fs模块读取文件(使用require语句)
var a = require('fs');
//readFIle方法读取文件内容
a.readFile('测试.txt',function(error,data){
console.log(error);
if(error === null){
console.log(data.toString());
} else {
console.log("文件错误,读取文件失败");
}
});

写入文件

1.使用require方法加载fs核心模块
2.使用writeFile函数读取文件

  • 包含三个参数

  • 第一个参数为文件路径及名称

  • 第二个参数为写入文本内容

  • 第三个参数为回调函数

  • 回调函数中的error参数
    文件写入成功 :errornull
    文件写入失败: error 就是错误对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var a = require('fs');
    a.writeFile('你是最棒的.md','你是最棒的',function(error) {
    console.log(error);
    if (error === null) {
    console.log("写入成功");
    } else {
    console.log("写入失败");
    }
    });

http

IP地址和端口号

  • IP地址用来定位计算机
  • 端口号用来定位具体应用程序
  • 所有需要联网的应用程序都需要占用一个端口号
  • 计算机默认端口号尽量不要访问
    • 80

服务器

  • 提供服务:为数据服务
  • 发请求
  • 接收请求
  • 处理请求
  • 反馈(发送响应)
  • 当客户端请求过来,就会自动触发服务器的request请求事件,然后执行第二个参数:回调处理函数

启动服务器

1.加载http模块
2.创建web服务对象
3.监听(注册)request事件
4.绑定端口号,启动服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//加载http模块
var http = require('http')

//创建web服务对象
var server = http.createServer()

//注册request事件
server.on('request',function(){
console.log('收到客户端请求')
})

//绑定端口号,启动服务
server.listen(3000,function(){
console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问')
})

request请求事件处理函数

  • request 请求事件处理函数,需要接收两个参数:request和response
  • request 请求对象:请求对象可以用来获取客户端的一些请求信息,例如请求路径
  • reponse响应对象:响应对象可以用来给客户端发送响应消息
  • 响应内容只能是二进制数据或者字符串
  • 转出数据内容为其他数据类型时需转化格式
  • 一个请求对应一个响应,已经结束响应则无法重复发送响应
  • 无请求就无响应
    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
    //加载http模块
    var http = require('http')
    //创建web服务
    var server = http.createServer()
    //监听request事件,设置请求处理函数,函数参数调用request和response
    server.on('request', function (request,response) {
    console.log('请求路径为' + request.url) //request.url为请求路径
    console.log('请求我的客户端的地址是:', request.socket.remoteAddress,
    request.socket.remotePort)
    //响应条件判断,判断请求路径

    //json测试
    var phone = [
    {
    name: 'apache',
    price: 80000,
    lcd: 'a'
    },
    {
    name: 'asdfg',
    price: 5000,
    lcd: 'd'
    },
    {
    name: 'hello',
    price: 25000,
    lcd: 'c'
    }]

    switch (request.url) {
    case '/':
    response.write('你好啊')
    response.end()
    break;
    case '/a':
    response.write('hello')
    response.end()
    break;
    case '/b':
    response.end('leihoua') //可以直接使用.end()函数
    break;
    case '/c':
    response.end(JSON.stringify(phone)) //转化为字符串格式
    break;
    default:
    response.write('当前路径未存在')
    response.end()
    break;
    }

    })
    //绑定端口号,启动服务
    server.listen(3000,function() {
    console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问')
    })


Content-type

  • 服务器默认发送的数据为utf8编码内容
  • 浏览器在不知道浏览器响应内容编码的情况下,会按当前操作系统默认编码解析
  • 中文操作系统默认编码为gbk
  • 在 http 协议中,Content-Type 就是用来告知对方我给你发送的数据内容是什么类型
  • 不同的资源对应的 Content-Type 是不一样
  • Content-Type工具:http://tool.oschina.net/commons
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//加载http模块
var http = require('http')
//创建web服务对象
var server = http.createServer()
//注册request监听事件
server.on('request',function (req,res) {
if(req.url === '/index') {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('hello,你好鸭')
} else if (req.url === '/html') {
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end('<p>来呀来呀,快来<a href="#">点我</a>鸭</p>')
}
// text/plain 就是普通文本
// 如果你发送的是 html 格式的字符串,则也要告诉浏览器我给你发送是 text/html 格式的内容
})
//绑定端口号,启动服务
server.listen('3000',function () {
console.log('server begin...')
})

返回文件(http-fs)

  • 发送的并不是文件,本质上来讲发送是文件的内容(一堆字符串)

  • 当浏览器收到服务器响应内容之后,就会根据你的 Content-Type 进行对应的解析处理

  • 结合 fs 发送文件中的数据

  • 字符数据需指定编码

  • 图片不需要指定编码

根据url返回内容

  1. 加载指定模块

  2. 创建服务对象

  3. 监听request事件

    • 判断请求的url
    • 为对应的url返回文件
    • 读取文件内容
    • 响应输出文件内容(注意转换格式与编码)
  4. 绑定端口号,启动服务

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
//加载指定模块
var fs = require('fs')
var http = require('http')
//创建服务对象
var server = http.createServer()
//监听request事件
server.on('request', function (req,res) {
var url = req.url;
if(url === '/index'){
//读取页面文件
fs.readFile('09-data/abc.html', function(err,data){
if(err) {
console.log('读取文件失败')
} else{
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(data.toString())
}
})
} else if (url === '/img') {
//读取图像文件
fs.readFile('09-data/601db542da7bc1d7ecbfb218685332a.jpg', function(err,data) {
if(err) {
console.log('无法读取该文件')
} else {
res.setHeader('Content-Type', 'image/jpeg')
res.end(data);
}
})
} else {
res.end('404')
}
})
//绑定端口号,启动服务
server.listen('3000', function() {
console.log('server is running')
})

浏览器输入路径打开文件

需求:用户在浏览器输入文件名即可访问该文件内容

  1. 将用户输入的文件转化为fs所找文件路径
  2. fs读取文件
  3. res响应文件内容
  4. 输出文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//引用模块
var http = require('http')
var fs = require('fs')
//创建服务
var server = http.createServer()
//监听request事件
server.on('request', function(req, res) {
var www = 'C:/Users/12524/Desktop/Node.JS'
var url = req.url //不能为中文路径
fs.readFile(www + url, "GBK", function(err, data) {
if (err) {
return res.end('404,nod found') //return停止代码执行
} else {
res.end(data)
}
})
})
//设置端口号,发布服务
server.listen('3000', function() {
console.log('server is running')
})

重定向

状态码301和302

301

  • 永久性的重定向,搜索引擎在抓取新内容的同时将旧网址替换为重定向之后的网址
    302

  • 暂时性跳转,搜索引擎抓取新的内容的同时保留旧网址

  • 服务器返回302时,搜索引擎认为新网址时暂时的

path

路径操作模块

基本操作

官方文档:https://nodejs.org/docs/latest-v13.x/api/path.html

  • path.basename:获取路径的文件名,默认包含扩展名
  • path.dirname:获取路径中的目录部分
  • path.extname:获取一个路径中的扩展名部分
  • path.parse:把路径转换为对象
    • root:根路径
    • dir:目录
    • base:包含后缀名的文件名
    • ext:后缀名
    • name:不包含后缀名的文件名
  • 🔺path.join:拼接路径(第二个参数会自动判断/是否多余)
  • path.isAbsolute:判断一个路径是否为绝对路径

body-parser

简介

body post解析中间件

处理程序之前,在中间件中对传入的请求体进行解析(response body)

处理post请求体

body-parser 提供四种解析器
JSON body parser
Raw body parser
Text body parser
URL-encoded form body parser

安装

1
npm i body-parser

引入

1
var bodyParse = require("body-parse")

配置

1
2
3
4
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())

使用

1
2
3
4
router.post('/login',function(req,res){
console.log(req.body)
})

原生获取post请求体方式

记得先引入querystring模块

querystring用作分割请求体内容并转化为对象格式

因为有时候会用到文件上传,所以这里要判断数据请求头的content-type,如果是multipart/form-data,则让formidable中间件去处理,否则自己处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.use((req, res, next) => {
let data = ``
if (req.method.toLowerCase() === 'get') {
return next()
}
// 如果是有文件的表单POST,则不处理
if (req.headers['content-type'].startsWith('multipart/form-data')) {
req.on('data', chunk => {
data += chunk
})
req.on('end', () => {
req.body = queryString.parse(data)
next()
})
}
})

node中的其他成员

__dirname() __filename

  • __dirname:动态获取当前模块文件所处目录的绝对路径
  • __filename: 动态获取当前文件的绝对路径
    在文件操作中,使用相对路径是不可靠的,因为node中文件操作的路径被设计为相对于执行node命令所处的路径。为了解决这个问题,需要使用__dirname()__filename把相对路径变为绝对路径(绝对路径不受任何影响)。

在拼接路径的过程中,为了避免手动拼接带来的一些错误,就可以使用path.join()来辅助拼接

1
2
3
4
5
6
7
8
9
10
11
12
13
var fs = require('fs')
var path = require('path')

//path会将路径名和文件名拼接起来且能识别适应文件名前路径形式
fs.readFile(path.join(__dirname, 'a.txt'), 'utf8', function(err, data) {
if (err) {
console.log('error')
} else {
console.log(data)
console.log(path.join(__dirname, './a.txt'))
}
})

注意:

模块中的路径标识和文件操作中的相对路径标识不一致

模块中的路径标识就是相对于当前文件模块,不受node命令所处路径影响

node中的模块系统

模块化

何为模块化

  • 文件作用域(模块是独立的,在不同的文件使用必须要重新引用)
    • 注意: 在node中没有全局作用域,它是文件模块作用域
  • 通信规则
    • 加载: require
    • 导出: exports

模块类型

  • 核心模块
    • 文件操作的fs
    • http服务操作的http
    • url路径操作模块
    • path路径处理模块
    • os操作系统信息
    • ……
  • 第三方模块
    • art-template
    • 必须通过npm来下载才可以使用
  • 自己写的模块
    • 自己创建的文件

模块标识

./相对于当前路径(在文件操作中可省略)

/在当前模块所处的磁盘根目录

javascript模块化(需补充)

  • Node 中的 CommonJS
  • 浏览器中的:
    • AMD require.js
    • CMD sea.js
  • es6中增加了官方支持

CommonJS模块规范

  • 模块作用域
  • 使用require方法来加载模块
  • 使用exports接口对象来导出模板中的成员

加载require

使用require函数加载模块

  • 若调用非核心模块和第三方模块,必须加上相对路径./,可以省略后缀名

  • require作用

    • 加载文件模块并执行里面的代码
    • 拿到被加载文件模块导出的接口对象
1
var http = require('http')

require方法加载规则

  • 优先从缓存加载(即不会重复调用同个模块中的函数)
    • 避免重复加载,提高模块加载效率
    • node会自动寻找当前文件路径的node_modules,从而加载第三方包,若没有,则会继续往上一级目录找,直到找到(如没找到则报错)

main.js

1
2
3
4
5
6
7
8
9
10
require('./a')

// 优先从缓存加载
// 由于 在 a 中已经加载过 b 了
// 所以这里不会重复加载
// 可以拿到其中的接口对象,但是不会重复执行里面的代码
var fn = require('./b')

console.log(fn)

a.js

1
2
3
4
5
console.log('a.js 被加载了')
var fn = require('./b')

console.log(fn)

b.js

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
console.log('b.js 被加载了')

module.exports = function () {
console.log('hello bbb')
}

````
### 第三方模块加载过程
art-template为例

1. 先找到当前项目路径的node_modules
2. node-modules/art-template
3. node-modules/art-template/package.json
4. 找到说明文件中的main属性
+ main属性记录art-template的入口模块
5. 加载使用这个第三方包
+ 实际加载的还是index.js文件
6. 若,package.json或main指定的入口模块不存在则node会找该目录下的index.js文件
7. 条件均不满足则自动往上一级目录查找


### 导出exports
使用exports函数导出需被外部访问的成员
+ exports
* 在每个文件模块中都提供了一个对象:exports
* exports 默认是一个空对象
* 将需被外部访问的成员挂载到这个 exports 对象中
导出多个成员

**方法一**

```js
exports.a = 123;
exports.b = function(){
console.log('bbb')
};
exports.c = {
foo:"bar"
};
exports.d = 'hello';

````
**方法二**

```js
module.exports = {
foo = 'hello',
add:function(){
return x+y;
}
};

````
导出单个成员(这时引用该模块,直接返回hello字符串)
```js
module.exports = 'hello';

````
案例:假设存在a模块和b模块,现在要用a模块访问b模块的成员

**a模块**

```js
console.log('hello,i am a');
var b = require('./b.js');//调用该模块的同时,将该模块内容引入
// require('./b.js');
console.log(b.bbb(10,20));
console.log('bye,a are leave');


````

**b模块**
```js
// var c = require('./c');
console.log('hello,i am b.');
require('./c');
exports.bbb = function(a,b) {
return a + b;
};
console.log('bye,b are leave');

````
**c模块**
```js
console.log('hello,i am c');
console.log('bye,c are leave');

````

### module.exportsexports的关系
真正要导出数据是module.exports,而node为了方便我们操作,所以指定了一个变量exports等同于module.exports,,如下代码所示

```js
var exports = module.exports

所以

1
2
exports.a = 2
exports.b = 3

这些值最终会等同于module.exports.a = 2和module.exports.a = 3,同时会被导出

但如果,你这样写代码

1
exports = 2

那么,你相当于改了exports的值,那么它就会与module.exports分道扬镳,它的值也不会等于module导出的值

所以,为了保险起见,新手最好使用module.exports导出你要导出的内容,避免错误