xiaohuihui
for me

Node笔记

2020-06-19 15:56:49
Word count: 6.7k | Reading time: 31min

Node笔记

Node.js中的JavaScript

node中的JavaScript是没有bom和dom的.

在node中为JavaScript提供了一些服务器级别api,比如文件操作的能力,服务器的能力.

运行一个hello world文件

1.创建编写Javascript脚本文件

2.打开终端,定位到脚本文件所属目录

3.输入node 文件名来执行对应的文件

Node读取文件

浏览器的JavaScript是没有操作文件的能力,但是node中JavaScript文件具有文件操作的能力.

在Node中如果想要进行文件操作,就必须引入fs这个核心模块,在fs这个核心模块中,就提供所有的文件操作的相关API.

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

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
//浏览器的javascript是没有文件操作的能力
//但是Node中的JavaScript具有文件操作能力

//fs是file-system的简写,就是文件系统
//在Node中如果想要进行文件操作,就提供了所有文件操作相关的API

//1.使用require方法加载fs核心模块
var fs = require('fs');

//读取成功:[data:数据;error:null]
//读取失败:[data:undefined;error:错误对象]
//2.读取文件
//第一个参数就是要读取文件的路径
//第二个参数是一个回调函数
fs.readFile('he.txt',function(error,data){
// if(error){
// console.log('文件读取失败')
// }else{
// console.log(data.toString());
// }
if(data){
console.log(data.toString());//因为文件中存储的其实都是二进制数据0和1,所以我们可以使用toString方法将其转化成我们能认识的字符
}else{
console.log('文件读取失败',error);
}
})

Node写入文件

1
2
3
4
5
6
7
8
9
10
var fs = require('fs');

//writeFile方法第一个参数为写入的文件名字,第二个参数为文件的内容
fs.writeFile('被Node写入的文件.txt','你好,我是被写入的Nodejs文件',function(error){ //这里回调函数只有一个参数
if(error){
console.log('文件写入失败');
}else{
console.log('文件写入成功');
}
})

创建http服务

使用Node构建一个Web服务器.

在Node中专门提供了一个核心模块:http

http模块帮你创建web服务器.

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
//使用Node构建web服务器
//在Node中专门提供了一个核心模块:http模块=>用来创建编写服务器

//1.加载http核心模块
var http = require('http');

//2.使用http.createServer()方法创建一个web服务器
var server = http.createServer()

//3.服务器要做:[提供对数据服务;发请求;发请求;接收请求;处理请求;发送响应;注册request请求事件;当客户端请求过来,就会触发服务器的request请求事件,然后执行第二个参数:回调处理函数]

//request:请求对象=>请请求对象用来获取客户端的一些信息
//response:响应对象=>可以用来给客户端发送响应信息
server.on('request',function(request,response){ //注册request请求事件
console.log('收到客户端的请求了,请求路径是:'+request.url)

//response对象有一个方法:write可以用来给客户端发送响应数据
//write可以使用多次,但是最后一次要用end来结束响应,不然客户端会一直等待
response.write('hello Nodejs');
//结束响应,告诉客户端,响应完毕,给你的主人们看吧哈哈哈
response.end();
})

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

灵活的服务器

灵活的服务器相当于根据不同的请求路径返回不同的数据

根据request.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
34
35
36
37
38
39
40
41
42
43
44
45
var http = require('http');
const { type } = require('os');


var server = http.createServer()


server.on('request',function(req,res){
console.log('收到客户端的请求了,请求路径是:'+req.url)
console.log('请求我客户端的地址是:'+req.socket.remoteAddress,req.socket.remotePort);

var requestUrl =req.url;
if(requestUrl === '/'){
res.end('index page');
}else if(requestUrl === '/login'){
res.write('this is a login page');
res.end('login page');
}else if(requestUrl === '/products'){
var prducts = [
{
name:'iphone x',
price:'8888',
type:'elcetronics'
},
{
name:'refrigerator',
price:4000,
type:'elcetronics'
},
{
name:'computer',
price:8000,
type:'elcetronics'
}
];
res.end(JSON.stringify(prducts)); //JSON.stringify()方法将数组转成字符串
}else{
res.end('404 Not Found');
}

})

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

Node中的核心模块

Node为js提供了很多服务器级别的api,这些api绝大多数都被包装到了一个具名的核心模块中.

比如文件操作的fs核心模块,http服务构建的http模块,path路径模块,os操作系统模块.

详情见https://nodejs.org/dist/latest-v14.x/docs/api/fs.html

Node中的模块系统

在Node中,模块有三种:

1.Node自带的模块

2.用户自己编写的文件模块,需要用require方法引入,引入的时候要用相对路径引入

3.第三方模块

在Node中,没有全局作用域,只有模块作用域.各个模块中的代码不相互影响.模块是完全封闭的,外部无法访问内部,内部无法访问外部。

模块之间的通信:在每个模块中都提供了一个对象:exports,该对象默认是一个空对象,你要做的就是把需要被外部访问使用的成员手动的挂载到exports对象中,然后使用require方法来加载模块,就可以的得到模块内部的exports接口对象。

image-20200619210731162

image-20200619210737530

image-20200619210742782

CommonJs模块化规范

node中模块系统:

1.模块作用域

2.使用require方法来加载模块

3.使用exports接口对象来导出模块中的成员

加载require

1
2
3
var 变量名 = require('模块');
//执行被加载模块中代码
//得到被加载模块中的exports接口对象

导出exports

node是模块作用域,默认文件中的所有成员只在当前文件模块有效

对于希望被其他模块访问的成员,我们就需要把公开的成员挂载到exports接口对象中,就可以导出多个成员了。

导出多个成员写法:

1
2
3
4
5
6
7
exports.a = 'hello';
exports.b = function(){
console.log('function')
};
exports.c = {
foo:'bar'
}

导出单个成员的写法

1
2
3
4
5
6
7
8
9
10
module.exports = 'hello';
module.exports = function add(x,y){
return x + y;
};
module.exports = {
add(x,y){
return x + y;
},
str:hello
}

image-20200623002103385

exports和module.exports的区别

image-20200623132841630

导出多个成员:exports.XXX = XXX或者module.exports={}

导出单个成员:module.exports = XXX

require加载规则

requre加载规则是有缓存的。

1.加载文件

加载自己创建的文件,require(‘./b’),要用绝对路径去引用

2.加载核心模块

​ 核心模块的本质也是文件,核心模块文件已被编译到了二进制文件中,只需要按照名字来加载就行了。比如require(‘fs’); require(‘http’)

3.第三方模块

第三方模块都需要npm来下载,使用的时候通过require(‘包名’)来加载使用。

1
var template = require('art-template')

说以下以上导入第三方模块的过程:

1.先找到当前文件所处目录中的node_modules目录(如果当前目录中没有node_modues文件,那么系统会自动往上层目录寻找)

2.然后找到node_modules/art-template

3.然后找到node.modules/art-template/package.json文件

4.然后找到node.modules/art-template/package.json文件中的main.js

5.main.js中就记录了art-template的入口模块,即可使用

它会找当前模块的node_module目录。

npm init命令用来初始化package包。

  • dependencies属性用来保存第三方包的依赖信息。
  • 在安装第三包的时候加上–sava。

npm install命令通过package.json文件来安装之间已经安装但是不小心删除了的node_modules。

npm

常用命令:

  • npm install –save 包名

    • 下载并保存依赖项(package.json)
  • npm uninstall –save 包名

    • 删除包
  • npm help

    • 查看使用帮助
  • npm install –save -dev

    • 开发时依赖不加-dev为运行时依赖。

ip地址和端口号

一般计算机的端口号的范围是0-65536之间

我们可以同时开启多个服务,但是要确保不同服务占用的端口号不一致才行。

关于解决中文编码识别不出问题

中文会出来乱码,原因是服务端默认发送的数据其实是utf8编码的内容,而浏览器是解析不了,所以我们要设置服务器响应的头部:

Content-Type:服务器最好把每次响应的数据的类型告诉浏览器。

1
res.setHeader('Content-Type','text/plain;charset=utf-8');   //设置编码,告诉浏览器编码格式然后解析

image-20200619224352516

在http协议中,Content-Type就是用来告诉对方我给你发送的数据是什么类型。

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
var http = require('http');
const { type } = require('os');


var server = http.createServer()


server.on('request',function(req,res){

console.log('收到客户端的请求了,请求路径是:'+req.url)
console.log('请求我客户端的地址是:'+req.socket.remoteAddress,req.socket.remotePort);

var requestUrl =req.url;
if(requestUrl === '/'){
res.end('index page');
}else if(requestUrl === '/login'){
res.setHeader('Content-Type','text/plain;charset=utf-8'); //设置编码,告诉浏览器编码格式然后解析
res.write('这是登录页面'); //此处中文会出来乱码,原因是服务端默认发送的数据其实是utf8编码的内容,而浏览器是解析不了的
res.end('login page');
}else if(requestUrl == '/html'){
res.setHeader('Content-Type','text/html;harset=utf-8'); //设置标签类型
res.end('<p>Hello world</p>点我<a href="#"></a>'); //如此浏览器会解析标签
}else if(requestUrl === '/products'){
var prducts = [
{
name:'iphone x',
price:'8888',
type:'elcetronics'
},
{
name:'refrigerator',
price:4000,
type:'elcetronics'
},
{
name:'computer',
price:8000,
type:'elcetronics'
}
];
res.end(JSON.stringify(prducts)); //JSON.stringify()方法将数组转成字符串
}else{
res.end('404 Not Found');
}

})


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

引入html

引入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
var http = require('http');
var fs = require('fs');

var server = http.createServer();

server.on('request',(req,res) => {
var url = req.url;
if(url === '/'){
fs.readFile('./index.html',function(err,data){
if(err){
res.setHeader('Content-Type','text/plain;charset=utf-8');
console.log('读取文件失败');
}else{
res.setHeader('Content-Type','text/html;harset=utf-8'); //设置标签类型
res.end(data);
}
})
}else if(url === '/pic'){
fs.readFile('./MVVM.jpg',function(err,data){
if(err){
res.setHeader('Content-Type','text/plain;charset=utf-8');
console.log('读取文件失败');
}else{
//图片不需要编码格式
res.setHeader('Content-Type','image/jpeg'); //设置标签类型
res.end(data);
}
})
}
})


server.listen(3000,function(){
console.log('服务器启动成功了,可以通过http://localhost:3000');
console.log('来访问');
})

读取目录

读取目录使用readdir方法进行读取

在Node中使用模板引擎

什么是模板引擎?模板引擎是为了使用户界面与业务数据(内容)分离而产生,它可以生成特定格式的文档,用于网站的的模板引擎就会生成一个标准的文档。

image-20200620155039205

自己制作模板引擎:

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
var http = require('http');
var fs = require('fs');
var server = http.createServer()

//设置根目录变量
var WWW = './www'

server.on('request',(req,res) => {
requestUrl = req.url;
if(requestUrl == '/'){
fs.readFile('./template.html',(err,data) => {
if(err){
return res.end('404,Not Found');
}
fs.readdir(WWW,function(err,files){
if(err){
return res.end('Can not find directory');
}else{
var content = '';
files.forEach(function(item){
//在es6中的``字符串中,可以用${}来引用变量
content += `
<tr>
<td>${item}/</td>
<td>46kb</td>
<td>2020-5-30</td>
</tr>`
})
data = data.toString(); //将data转化为字符串格式
data = data.replace('main',content);
//发送解析过后的
res.end(data);
}

})


})


}else if(requestUrl == '/register'){
res.end('this is a register page');
}else{
res.end('404 Not Found');
}
})

server.listen(3000,() => {
console.log('service is runing,please click http://localhost:3000 to access');
})

art-template模板

安装模板:

1
npm install art-template --save		//运行时依赖

引用模板:

1
var template = require('art-template')

模板引擎解析出来的都是字符串,所以模板引擎是不关心内容的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var template = require('art-template');   //引用模板
var fs = require('fs');

fs.readFile('./tpl.html',function(err,data){
if(err){
return console.log('读取文件失败');
}
data = data.toString();
var result = template.render(data,{
name:'xhh',
age:20,
address:'云南大理'
})
console.log(result);
})

tpl.html文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>{{name}}</h1>
<h1>{{age}}</h1>
<h1>{{address}}</h1>
</body>
</html>

最终渲染结果:

image-20200621000116552

所以模板引擎是不关心内容的,它解析的都是字符串。

使用art-template模板替换先前写的原生模板引擎:

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
var http = require('http');
var fs = require('fs');
var server = http.createServer();
var template = require('art-template'); //引用模板

//设置根目录变量
var WWW = './www'

server.on('request',(req,res) => {
requestUrl = req.url;
if(requestUrl == '/'){
fs.readFile('./apache-file.html',(err,data) => {
if(err){
return res.end('404,Not Found');
}
fs.readdir(WWW,function(err,files){
if(err){
return res.end('Can not find directory');
}else{
var htmlresult = template.render(data.toString(),{
title:'哈哈',
files:files //此处的files是指apache-file.html文件定义的变量
})
//发送解析过后的
res.end(htmlresult);
}
})
})


}else if(requestUrl == '/register'){
res.end('this is a register page');
}else{
res.end('404 Not Found');
}
})

server.listen(3000,() => {
console.log('service is runing,please click http://localhost:3000 to access');
})

apache-file.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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
<style>
table,th, td{
border: 1px solid black;
text-align: center;
}
</style>
</head>
<body>
<h2>F:\Node文件\第二天\www的索引</h2>
<table style="border: 1px solid blue;width: 500px;">
<tr>
<th>名称</th>
<th>大小</th>
<th>修改日期</th>
</tr>
{{each files}} <!--模板引擎中使用each来遍历数组-->
<tr>
<td>{{$value}}/</td>
<td>46kb</td>
<td>2020-5-30</td>
</tr>
{{/each}}
</table>
</body>
</html>

客户端渲染和服务端渲染

客户端渲染不利于SEO搜索引擎优化

服务端渲染是可以被爬虫抓取到的,客户端渲染(异步)是很难被爬虫抓取的

所以真正的网站不是纯异步也不是纯服务端渲染出来的。

服务端渲染:在服务端使用模板引擎

静态资源的引入

统一把静态资源放到public目录下,然后再readFile()方法来进行访问。

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
var http = require('http')
var fs = require('fs')


http
.createServer(function(req,res){
var url = req.url;
if(url === '/'){
fs.readFile('./views/index.html',(err,data) => {
if(err){
return res.end('404 Not Found')
}
res.end(data);
})
}else if(url.indexOf('/public/') === 0){
fs.readFile('.'+url,function(err,files){ //访问./public
if(err){
return res.end('文件不存在');
}
res.end(files);
})
}else{
fs.readFile('./views/404.html',(err,data) => {
if(data){
res.end(data)
}
})
}
})

.listen(3000,function(){
console.log('Service is running,please access http://localhost:3000')
})

Express

Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

先安装Express框架:

1
npm install --save express

使用框架后的写服务器代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var express = require('express')

var app = express();

//公开指定目录
//这样做之后,就不用再访问目录,直接通过/public/xx的方式访问public目录下的资源,直接读取目录
app.use('/public/',express.static('./public/'));

app.get('/',function(req,res){
res.send('hello world')
})

app.get('/login',function(req,res){
res.send('这是关于页面')
})


app.listen(3000,function(){
console.log('service is running,please access '+ 'http://localhost:3000 '+' to access')
})

express中使用模板引擎

安装art-template

1
2
npm install --save art-template
npm install --save express-art-template

使用art-template

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
var express = require('express')

var app = express();

//公开指定目录
//这样做之后,就不用再访问目录,直接通过/public/xx的方式访问public目录下的资源,直接读取目录
app.use('/public/',express.static('./public/'));

//第一个参数表示当渲染.该参数为后缀的文件,使用art-template模板引擎
//express有一个约定,开发人员把所有的视图文件都放到views文件夹中
app.engine('html',require('express-art-template'));


app.get('/',function(req,res){
res.render('hello.html',{
title:'管理系统'
});
})

app.get('/login',function(req,res){
res.send('这是登录页面')
})
app.get('/about',function(req,res){
res.send('这是关于页面')
})


app.listen(3000,function(){
console.log('service is running,please access '+ 'http://localhost:3000 '+' to access')
})

image-20200705133405178

如果想要修改默认的views目录,则使用app.set(‘views’,render函数的默认路径)

在express中获取表单GET请求参数

1
req.query

在express中获取表单POST请求体数据

在express中没有内置获取表单请求体的api,这里我们需要使用一个第三方包:body-parser

安装:

1
npm install --save body-parser

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var express = require('express')
//引入模块
var bodyParser = require('body-parser')

var app = express()

//只要加入以下配置,则在req请求对象上会多出来一个属性:body,也就是说可以直接通过req.body来获取表单提交的post数据
app.use(bodyParser.urlencoded({ extended: false }))

// parse application/json
app.use(bodyParser.json())

app.use(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
res.end(JSON.stringify(req.body, null, 2))
})

修改完代码自动重启服务器

nodemon是一个基于nodejs开发的一个第三方命令行工具,作用是解决每次修改完代码后重启服务器的问题。

安装:

1
2
npm install --global nodemon
//全局安装就是在任意目录都可以安装

通过nodemon 文件名 来启动服务,当文件发生修改的时候,自动重启服务器。

回调函数

1
2
3
4
5
6
7
8
9
10
11
function test(callback){
var a;
setTimeout(function(){
a = 10;
callback(a)
},1000)
}
test(callback)
function callback(result){
console.log(result)
}
1
2
3
4
5
6
7
8
9
10
11
function add(x,y,callback){
var ret;
setTimeout(function(){
ret = x+y
callback(ret)
},1000)
console.log(ret)
}
add(10,20,function(ret){
console.log(ret)
})

js异步在于单线程。如果不异步得卡死掉。

ajax是如何分装的?

1
2
3
4
5
6
7
8
9
<script>
var xhr = new XMLHttpRequest();
//当请求加载成功后调用指定的函数
xhr.onload = function(){
console.log(xhr.responseText)
}
xhr.open("get","data.json",true)
xhr.send()
</script>

image-20200709150906826

经过异步操作之后:

核心:往里传回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
function get(url,callback){
var xhr = new XMLHttpRequest();
xhr.onload = function(){
callback(xhr.responseText)
}
xhr.open("get",url,true)
xhr.send()
}


get('data.json',function(data){
console.log(data)
})
</script>

输出结果是一样的。

ES6 find和findIndex源码的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var user = [
{id:1,name:'xhh',age:12},
{id:2,name:'xhh2',age:12},
{id:3,name:'xhh3',age:12},
{id:4,name:'xhh4',age:12},
]

Array.prototype.myFind = function(condition){
for(var i=0;i<this.length;i++){
if(condition(this[i])){
return this[i]
}
}
}

var ret = user.myFind(function(item){
return item.id === 4
})
console.log(ret) //{id: 4, name: "xhh4", age: 12}
1
2
3
4
5
6
7
8
9
10
11
12
Array.prototype.myFind = function(condition){
for(var i=0;i<this.length;i++){
if(condition(this[i])){
return i
}
}
}

var ret = user.myFind(function(item){
return item.id === 4
})
console.log(ret) //3

MongoDB数据库

关系型数据库与非关系型数据库

关系型数据库

  • 所有的关系型数据库都需要通过sql语言来执行
  • 所有的关系型数据库在操作之前都需要设计表结构
  • 数据表支持约束
    • 唯一
    • 主键
    • 默认值
    • 非空

非关系型数据库

启动和关闭数据库

mongoDB默认执行mongod命令所处盘符根目录下的 /data/db 作为自己的数据存储目录。所以在第一次执行该命令之前先自己手动创建一个 /data/db。

如果想要修改默认的数据存储目录,可以

1
mongod --dbpath=数据存储路径目录
  • 连接数据库:mongo
  • 退出连接:exit

基本命令

  • show dbs
    • 查看所有数据库
  • use数据库名称
    • 切换到指定的数据
  • 插入数据

在node中操作mongoDB

使用第三方包mongooes来操作mongoDB数据库。

Node操作mysql数据库

安装:npm install mysql

用法:

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
var mysql      = require('mysql');
//1.创建连接
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'admin',
database : 'nodetest'
});

//连接数据库
connection.connect();

//执行数据库
connection.query('SELECT * FROM `users`', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results);
});


// connection.query('INSERT INTO users(username,password) VALUES("admin","123456")', function (error, results, fields) {
// if (error) throw error;
// console.log('The solution is: ', results);
// });

//关闭连接
connection.end();

回调地狱

回调地狱的形成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var fs = require('fs')

fs.readFile('./file/a.txt','utf8',function(err,data){

console.log(data)
})


fs.readFile('./file/b.txt','utf8',function(err,data){
if(err) throw err
console.log(data)
})


fs.readFile('./file/c.txt','utf8',function(err,data){
if(err) throw err
console.log(data)
})

image-20200711154059909

因为js的readFile方法是异步代码,所以执行文件之后,它并不是按顺序输出的。

解决的方法是进行嵌套,按顺序执行:

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

fs.readFile('./file/a.txt','utf8',function(err,data){
if(err) throw err
console.log(data);
fs.readFile('./file/b.txt','utf8',function(err,data){
if(err) throw err
console.log(data);
fs.readFile('./file/c.txt','utf8',function(err,data){
if(err) throw err
console.log(data)
})
})
})

如果出现嵌套过多的话,代码会没有可读性,不易维护。

Promise

为了解决以上编码方式代码的问题(回调地狱嵌套),所以在ES6中新增了一个API:Promise

image-20200711160851616

Promise的使用方式

image-20200711163513740

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
var fs = require('fs')

var p1 = new Promise(function(resolve,reject){
fs.readFile('./file/a.txt','utf8',function(err,data){
if(err) reject(err)
resolve(data)
})
})


var p2 = new Promise(function(resolve,reject){
fs.readFile('./file/b.txt','utf8',function(err,data){
if(err) reject(err)
resolve(data)
})
})


var p3 = new Promise(function(resolve,reject){
fs.readFile('./file/c.txt','utf8',function(err,data){
if(err) reject(err)
resolve(data)
})
})




//p1就是Promise,p1成功以后,然后then指定操作
//then方法接收的function就是容器中的resolve函数
p1.then(function(data){
console.log(data)
return p2 //return一个Promise对象,然后后续再用then执行对p2的操作
},function(err){
console.log(err)
}).then(function(data){
console.log(data)
return p3
},function(err){
console.log(err)
}).then(function(data){
console.log(data)
},function(err){
console.log(err)
})

image-20200711165047414

封装Promise方法
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
var fs = require('fs')

function pReadFile(filePath){
return new Promise(function(resolve,reject){
fs.readFile(filePath,'utf8',function(err,data){
if(err) reject(err)
resolve(data)
})
})
}


pReadFile('./file/a.txt')
.then(function(data){
console.log(data)
return pReadFile('./file/b.txt')
},function(data){
console.log(data)
})
.then(function(data){
console.log(data)
return pReadFile('./file/c.txt')
},function(err){
console.log(err)
})
.then(function(data){
console.log(data)
},function(err){
console.log(err)
})

Promise案例

data.json文件:

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
{
"users":[
{
"id":1,
"username":"admin",
"age":18,
"job": 3
},
{
"id":2,
"username":"admin2",
"age":18,
"job": 2
},
{
"id":3,
"username":"admin3",
"age":18,
"job": 1
}
],
"jobs":[
{
"id":1,
"name":"学生"
},
{
"id":2,
"name":"程序员"
},
{
"id":3,
"name":"司机"
},
{
"id":4,
"name":"画家"
},
{
"id":5,
"name":"医生"
}
]
}

主文件:

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form id="user_from">

</form>
<script type="text/template" id="tpl">
<div>
<label for="">用户名</label>
<input type="text" name="" id="" value="{{user.username}}">
</div>
<div>
<label for="">年龄</label>
<input type="text" name="" id="" value="{{user.age}}">
</div>
<div>
<label for="">职业</label>
<select name="" id="">
{{each jobs}}
{{if user.job === $value.id}}
<option value="{{$value.id}}" selected>{{$value.name}}</option>
{{else}}
<option value="{{$value.id}}">{{$value.name}}</option>
{{/if}}
{{/each}}
</select>
</div>
</script>

<script src="node_modules/art-template/lib/template-web.js"></script>

<script>
//有两个接口,分别是用户表和职业信息表
//其中一个接口获取用户信息,另一个接口获取所有的职业信息

get('http://localhost:3000/users/3',function(userData){
get('http://localhost:3000/jobs',function(jobsData){
user = JSON.parse(userData)
jobs = JSON.parse(jobsData)
console.log(user)
console.log(jobs)
var htmlStr = template('tpl',{
user:user,
jobs:jobs
})
console.log(htmlStr)
document.querySelector('#user_from').innerHTML = htmlStr
})
})


//封装好的ajax方法
function get(url,callback){
var xhr = new XMLHttpRequest();
xhr.onload = function(){
callback(xhr.responseText)
}
xhr.open("get",url,true)
xhr.send()
}
</script>
</body>
</html>

渲染结果:

image-20200712141611512

以上方式容易产生回调地狱,所以使用Promise改进:

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form id="user_from">

</form>
<script type="text/template" id="tpl">
<div>
<label for="">用户名</label>
<input type="text" name="" id="" value="{{user.username}}">
</div>
<div>
<label for="">年龄</label>
<input type="text" name="" id="" value="{{user.age}}">
</div>
<div>
<label for="">职业</label>
<select name="" id="">
{{each jobs}}
{{if user.job === $value.id}}
<option value="{{$value.id}}" selected>{{$value.name}}</option>
{{else}}
<option value="{{$value.id}}">{{$value.name}}</option>
{{/if}}
{{/each}}
</select>
</div>
</script>

<script src="node_modules/art-template/lib/template-web.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>

<script>
//使用jquery的ajax方式
var data = {}
$.get('http://localhost:3000/users/2')
.then(function(user){
data.user = user
return $.get('http://localhost:3000/jobs')
})
.then(function(jobs){
data.jobs = jobs
var htmlStr = template('tpl',{
user:data.user,
jobs:data.jobs
})
console.log(data)
console.log(htmlStr)
document.querySelector('#user_from').innerHTML = htmlStr
})
</script>
</body>
</html>

封装Promise版的ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//封装Promise版的ajax方法
function get(url,callback){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.onload = function(){
resolve(JSON.parse(xhr.responseText))
callback && callback(JSON.parse(xhr.responseText)) //此处也可以直接使用回调函数的方式去调数据
}
xhr.onerror = function(err){
reject(err)
}
xhr.open("get",url,true)
xhr.send()
})
}

然后直接以Promise的方式去调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var data = {}
get('http://localhost:3000/users/2')
.then(function(user){
data.user = JSON.parse(user)
return $.get('http://localhost:3000/jobs')
})
.then(function(jobs){
data.jobs = jobs
var htmlStr = template('tpl',{
user:data.user,
jobs:data.jobs
})
console.log(data)
console.log(htmlStr)
document.querySelector('#user_from').innerHTML = htmlStr
})

或者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var data = {}
get('http://localhost:3000/users/2',function(user){
data.user = user
return get('http://localhost:3000/jobs',function(jobs){
data.jobs = jobs
var htmlStr = template('tpl',{
user:data.user,
jobs:data.jobs
})
console.log(data)
console.log(htmlStr)
document.querySelector('#user_from').innerHTML = htmlStr
})
})

json-server

安装:npm install -g json-server

启动json-server
1
json-server --watch json文件名

默认3000端口,当然我们也可以指定端口,这样就可以监听多个json文件:

1
json-server --watch -port 8888 json文件名

Author: 小灰灰

Link: http://xhh460.github.io/2020/06/19/Node%E7%AC%94%E8%AE%B0/

Copyright: All articles in this blog are licensed.

< PreviousPost
js笔记
NextPost >
数据结构
CATALOG
  1. 1. Node笔记
    1. 1.0.1. Node.js中的JavaScript
    2. 1.0.2. 运行一个hello world文件
    3. 1.0.3. Node读取文件
    4. 1.0.4. Node写入文件
    5. 1.0.5. 创建http服务
    6. 1.0.6. 灵活的服务器
    7. 1.0.7. Node中的核心模块
    8. 1.0.8. Node中的模块系统
    9. 1.0.9. CommonJs模块化规范
      1. 1.0.9.0.1. exports和module.exports的区别
      2. 1.0.9.0.2. require加载规则
  2. 1.0.10. npm
  3. 1.0.11. ip地址和端口号
  4. 1.0.12. 关于解决中文编码识别不出问题
  5. 1.0.13. 引入html
  6. 1.0.14. 读取目录
  7. 1.0.15. 在Node中使用模板引擎
  8. 1.0.16. 客户端渲染和服务端渲染
  9. 1.0.17. 静态资源的引入
  10. 1.0.18. Express
    1. 1.0.18.1. express中使用模板引擎
    2. 1.0.18.2. 在express中获取表单GET请求参数
    3. 1.0.18.3. 在express中获取表单POST请求体数据
  11. 1.0.19. 修改完代码自动重启服务器
  12. 1.0.20. 回调函数
  13. 1.0.21. ajax是如何分装的?
  14. 1.0.22. ES6 find和findIndex源码的封装
  15. 1.0.23. MongoDB数据库
    1. 1.0.23.1. 关系型数据库与非关系型数据库
    2. 1.0.23.2. 启动和关闭数据库
    3. 1.0.23.3. 基本命令
    4. 1.0.23.4. 在node中操作mongoDB
  16. 1.0.24. Node操作mysql数据库
  17. 1.0.25. 回调地狱
    1. 1.0.25.0.1. 回调地狱的形成
    2. 1.0.25.0.2. Promise
    3. 1.0.25.0.3. Promise的使用方式
    4. 1.0.25.0.4. 封装Promise方法
  • 1.0.26. Promise案例
  • 1.0.27. json-server
    1. 1.0.27.0.1. 启动json-server