# nodejs入门
# nodejs能解决什么问题?
- 实现高并发用户连接
- 实现高性能服务器
- 非阻塞I/O及事件循环机制
# nodejs安装
# 先实现一个helloworld热个身
- 新建一个helloworld.js
- 使用require引入模块
- 使用http.createServer()方法创建服务器
- 使用listen来绑定
8000
端口 - 通过request,response来接收和响应参数,代码如下:
var http = require("http");
http.createServer((request, response) => {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, { 'Content-Type': 'text/plain' });
//发送响应数据
response.end('hello world')
}).listen(8000);
console.log('server running at http://127.0.0.1:8000');
2
3
4
5
6
7
8
9
10
- 打开该路径下的终端,输入命令
node helloworld.js
- 打开浏览器访问
http://127.0.0.1:8000
# 模块
编写稍微大一点的程序时会将代码模块化,在NodeJS中,一般将代码合理拆分到不同的JS文件中,每一个文件就是一个模块,而文件路径就是模块名。
都有reuqire
exports
module
三个预先定义好的变量可供使用。
# require
用于在当前模块中加载和使用别的模块,传入模块名,返回一个模块导出对象,可以用相对路径,可以用绝对路径,文件名后的.js
可以省略,也可以加载和使用JSON
文件举例:
var foo1=reuqire('./foo');
var foo2=reuqire('./foo.js');
var foo3 = require('/home/user/foo');
2
3
# exports
用于导出模块公有方法和属性。别的模块通过require函数使用当前模块时得到的就是当前模块的exports对象:
exports.hello=function(){
console.log('hello');
}
2
3
# module
可以访问到当前模块的一些相关信息,但更多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。
module.exports=function(){
console.log('hello');
}
2
3
# 小案例
- 新建俩个js:
counter.js
、node.js
- 在counter.js中输入:
var i = 0;
function count() {
return ++i;
};
exports.counter = count;
2
3
4
5
6
- 在node.js中输入
var count1 = require('./counter');
var count2 = require('./counter');
console.log(count1.counter());
console.log(count2.counter());
console.log(count2.counter());
2
3
4
5
- 在终端输入
node node.js
,结果如下:
$ node main.js
1
2
3
2
3
4
# 模块路径解析规则
require
函数支持第三种形式的路径,写法类似于foo/bar,并依次按照以下规则解析路径,直到找到模块位置
- 内置模块
- node_modules目录
- NODE_PATH环境变量
# 文件操作
NodeJS提供了基本的文件操作API
# demo-小文件拷贝
fs.readFile(src1, function(err, data) {
if (err) throw new Error('something wrong was happended');
fs.writeFile(src2, data, function(err) {
if (err) throw new Error('something wrong was happended');
})
})
2
3
4
5
6
# API简述
# Buffer数据块
Js语言自身只有字符串类型,没有二进制,因此NodeJS提供了一个与String对等的全局构造函数Buffer来提供对二进制数据的操作。除了可以读取文件得到Buffer的实例外,还能够直接构造。
# Stream数据流
当内存中无法一次装下需要处理的数据时,或者一边读取一边处理更加高效时,我们就需要用到数据流。NodeJS中通过各种Stream来提供对数据流的操作。
# fs文件系统
fs模块提供的API基本可以分为以下三类:
- 文件读写属性,常用的有
fs.stat
、fs.chmod
、fs.chown
等 - 文件内容读写,常用的有
fs.readFile
、fs.readdir
、fs.writeFile
、fs.mkdir
等 - 底层文件操作,常用的有
fs.open
、fs.read
、fs.close
等
# path路径
path 模块提供了一些实用工具,用于处理文件和目录的路径
# 网络操作
# http
'http'模块提供两种使用方式:
- 作为服务端使用时,创建一个HTTP服务器,监听HTTP客户端请求并返回响应。
- 作为客户端使用时,发起一个HTTP客户端请求,获取服务端响应。
# https
https模块与http模块极为类似,区别在于https模块需要额外处理SSL证书。 在服务端模式下,创建一个HTTPS服务器的示例如下。
var options = {
key: fs.readFileSync('./ssl/default.key'),
cert: fs.readFileSync('./ssl/default.cer')
};
var server = https.createServer(options, function (request, response) {
// ...
});
2
3
4
5
6
7
8
# URL
一个完整的URL的各组成部分:
href
-----------------------------------------------------------------
host path
--------------- ----------------------------
http: // user:pass @ host.com : 8080 /p/a/t/h ?query=string #hash
----- --------- -------- ---- -------- ------------- -----
protocol auth hostname port pathname search hash
------------
query
2
3
4
5
6
7
8
9
- 可以使用
.parse
将一个URL字符串转换成URL对象 .format
方法将一个URL对象转换为URL字符串.resolve
方法可以用于拼接URL
# querystring
用于实现URL参数字符串与参数对象的互相转换
querystring.parse('foo=bar&baz=qux&baz=quux&corge');
/* =>
{ foo: 'bar', baz: ['qux', 'quux'], corge: '' }
*/
querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
/* =>
'foo=bar&baz=qux&baz=quux&corge='
*/
2
3
4
5
6
7
8
9
# Zlib
提供了数据压缩和解压的功能。当我们处理HTTP请求和响应时,可能需要用到这个模块
# net
net 模块用于创建基于流的 TCP 或 IPC 的服务器(net.createServer())与客户端(net.createConnection())。
# 进程管理
# Process
process 对象是一个全局变量,提供了有关当前 Node.js 进程的信息并对其进行控制
在NodeJS中可以通过process.argv获取命令行参数。但是比较意外的是,node执行程序路径和主模块文件路径固定占据了argv[0]和argv[1]两个位置,而第一个命令行参数从argv[2]开始。为了让argv使用起来更加自然,可以按照以下方式处理。
function main(argv) {
// ...
}
main(process.argv.slice(2));
2
3
4
# child_process
使用child_process模块可以创建和控制子进程。该模块提供的API中最核心的是.spawn,其余API都是针对特定使用场景对它的进一步封装,算是一种语法糖。
# cluster
cluster模块是对child_process模块的进一步封装,专用于解决单进程NodeJS Web服务器无法充分利用多核CPU的问题。使用该模块可以简化多进程服务器程序的开发,让每个核上运行一个工作进程,并统一通过主进程监听端口和分发请求。
# 框架express
# 入门
1. mkdir myapp
2. cd myapp
3. npm init -y
4. npm install express
2
3
4
# Express生成器
使用express-generator
快速创建应用程序框架
1. npm install express-generator -g
2. express myapp
3. cd myapp
4. npm install
5. npm start
2
3
4
5
生成的应用程序具有以下目录:
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.pug
├── index.pug
└── layout.pug
7 directories, 9 files
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 路由
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('hello');
})
app.listen(3000);
2
3
4
5
6
# app.route()
var express = require('express');
var http=require('http');
var app = express();
app.route('/book').get(function(req, res) {
res.send('Get a random book');
});
http.createServer(app).listen(3000);
2
3
4
5
6
7
# express.Router
var express = require('express');
var router = express.Router();
// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// define the home page route
router.get('/', function(req, res) {
res.send('Birds home page');
});
// define the about route
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Request方法
# Response方法
举例:
//查询
router.post('/', function(req, res) {
List.find({}, (err, listDoc) => {
if (err) {
res.json({
status: '1',
msg: '查询失败',
result: ''
})
} else {
res.json({
status: '0',
msg: '查询成功',
result: listDoc
})
}
})
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//添加网址
router.post('/addWeb', function(req, res) {
let titleItem = req.body.titleItem;
let link = req.body.link,
name = req.body.name,
description = req.body.description;
if (!titleItem) {
res.json({
status: '1',
msg: '数值为空',
result: ''
})
return false
}
List.updateOne({ title: titleItem }, {
$push: {
'children': {
'link': link,
'title': name,
'description': description
}
}
}, (err, listDoc) => {
if (err) {
res.json({
status: '1',
msg: '插入失败',
result: ''
})
} else {
res.json({
status: '0',
msg: '插入成功',
result: listDoc
})
}
})
});
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
# 中间件
中间件函数是可以访问请求对象(req)、响应函数(res)以及应用程序请求————响应周期中的next函数,next函数是Express路由器的一个函数,当被调用时,它会在当前中间件之后执行中间件。
# demo-配置一个中间件函数
var express = require('express')
var app = express()
var requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
app.use(requestTime)
app.get('/', function (req, res) {
var responseText = 'Hello World!<br>'
responseText += '<small>Requested at: ' + req.requestTime + '</small>'
res.send(responseText)
})
app.listen(3000)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 应用程序中间件
使用app.use()
和app.Mthod()
函数将程序级中间件绑定到app对象的实例
要从路由器中间件堆栈跳过其余的中间件函数,请调用next('route')将控制权传递给下一个路由,注意:next('route')仅适用于使用app.METHOD()或router.METHOD()函数加载的中间件函数。
app.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next route
if (req.params.id === '0') next('route')
// otherwise pass the control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// send a regular response
res.send('regular')
})
// handler for the /user/:id path, which sends a special response
app.get('/user/:id', function (req, res, next) {
res.send('special')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
# 路由器级中间件
var app = express()
var router = express.Router()
// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next router
if (req.params.id === '0') next('route')
// otherwise pass control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// render a regular page
res.render('regular')
})
// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
console.log(req.params.id)
res.render('special')
})
// mount the router on the app
app.use('/', router)
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
# 内置中间件
从版本4.x开始,Express不再依赖于Connect,之前包含在Express中的中间件函数现在位于不同的模块中,查看中间件函数列表。
Express具有以下内置中间件函数:
- express.static提供静态资源,如HTML文件、图像等。
- express.json使用JSON的有效负载解析传入的请求,注意:适用于Express 4.16.0+。
- express.urlencoded使用URL编码的有效负载解析传入的请求,注意:适用于Express 4.16.0+。
# 第三方中间件
使用第三方中间件为Express应用程序添加功能。
安装Node.js模块以获得所需的功能,然后在应用程序级别或路由器级别将其加载到你的应用程序中。
以下示例说明了安装和加载cookie解析中间件函数cookie-parser。
$ npm install cookie-parser
var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')
// load the cookie-parsing middleware
app.use(cookieParser())
2
3
4
5
6
# 静态文件
要提供静态文件(如images、CSS文件和JavaScript文件),请使用Express中的express.static内置中间件功能,函数签名是:
express.static(root, [options])
//example,加载public目录下的文件
app.use(express.static(path.join(__dirname, 'public')))
//http://localhost:3000/images/kitten.jpg
//http://localhost:3000/css/style.css
//http://localhost:3000/js/app.js
2
3
4
5
6
# 其他问题
# 如何处理404响应?
app.use(function (req, res, next) {
res.status(404).send("Sorry can't find that!")
})
2
3
# 如何设置错误处理程序?
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
2
3
4
# 如何呈现纯html?
没有必要使用res.render()
函数“渲染”HTML,如果你有特定文件,请使用res.sendFile()
函数,如果要从目录提供许多资源,请使用express.static()
中间件函数。
# express实例
上传图片功能,调用的阿里接口
const express = require('express')
const app = express()
var multiparty = require('multiparty');
var fs = require('fs');
var request = require('request');
var apiRoutes = express.Router()
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", ' 3.2.1')
if (req.method == "OPTIONS") res.send(200); /*让options请求快速返回*/
else next();
});
apiRoutes.post('/ali', function(req, res) {
var path = "";
var form = new multiparty.Form(); //实例化表单
var url = 'https://kfupload.alibaba.com/mupload';
form.uploadDir = 'upload'; //定义上传路径为根目录/upload
//上传完成后处理
form.parse(req, function(err, fields, files) {
console.log(files);
if (files) {
path = files.file[0].path;
} else {
console.log("没图片");
}
upload(path, url, res);
});
});
function upload(path, url, res) {
var options = {
'method': 'POST',
'url': url,
'headers': {},
formData: {
'name': path,
'scene': 'aeMessageCenterV2ImageRule',
'file': {
'value': fs.createReadStream(path),
'options': {
'filename': 'filename',
'contentType': null
}
}
}
};
request(options, function(error, response) {
if (error) throw new Error(error);
res.send(response.body);
});
}
app.use('/pic', apiRoutes)
const port = process.env.PORT || 4000
module.exports = app.listen(port, function(err) {
if (err) {
console.log(err)
return
}
console.log('Listening at http://localhost:' + port + '\n')
})
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