node.js入门基础

内容:

1.node.js介绍

2.node.js内置常用模块

3.node.js数据交互

 

 

 

一、node.js介绍

(1)node.js特点

与其他语言相比,有以下优点:

  • 对象、语法和JavaScript一模一样,易于前端使用
  • 相比PHP、Java、python,性能还可以
  • 前后端配合方便
  • 非阻塞、异步的交互

当然,也有缺点:比如说库支持不如Java丰富,和js一样是单线程单进程

 

(2)node.js安装

下载对应你系统的Node.js版本:https://nodejs.org/en/download/,选安装目录进行安装即可
安装完毕测试如下:

 

(3)node.js用处

  • 服务器 - 小型后台系统、中间层
  • 做工具(测试、构建、抓取) - grunt、gulp、WebPack 

 

(4)运行node

创建一个node文件夹,然后在文件夹下写入1.js:

1 let a = 12;
2 let b = 5;
3 
4 console.log(a+b)

然后在命令行中进入node目录下运行该文件:node 1.js  注:node运行文件:node  xxx.js

运行结果如下:

 

 

 

二、node.js内置常用模块

1.断言——assert

 1 const assert = require("assert")
 2 
 3 function sum(a, b){
 4     // assert(判断, "xxx")  判断为假输出后面的信息
 5     assert(arguments.length==2, "必须传两个参数")
 6     assert(typeof a == 'number', "第一个参数必须是数字")
 7     assert(typeof b == 'number', "第二个参数必须是数字")
 8     
 9     return a+b
10 }
11 
12 console.log(sum(2, 5))
13 // 执行下面人任意一句将报错:
14 // console.log(sum(2, '1'))
15 // console.log(sum(3))

 


2.Buffer和File System模块

  • buffer:曾经是node中的模块,后来融入到node本身之中了,处理二进制
  • file system:处理文件(读写文件)

file system使用实例:

 1 const fs = require("fs")
 2 
 3 // 读取文件
 4 fs.readFile('1.txt', function(err, data){
 5     if(err){
 6         console.log("有错!");
 7     } else {
 8         console.log(data);
 9         console.log(data.toString());
10     }
11     
12 })  
13 
14 // 写文件
15 fs.writeFile('2.txt', 'xxx', function(err){
16     if (err) {
17         console.log(err);
18     } else {
19         console.log("成功!");
20     }
21 })

注意:图片不要将二进制转成字符串,这样做会导致图片格式丢失

获取文件详细信息:

 1 const fs = require('fs')
 2 
 3 fs.stat('1.txt', function (err, stat) {
 4     if(err){
 5         console.log('获取文件信息失败')
 6     } else{
 7         console.log(stat)                           // detail info
 8         console.log(stat.mtime.toGMTString())       // 修改时间
 9     }
10 })

Buffer基础使用:

1 let a = new Buffer('abc');
2 let b = new Buffer('ddd');
3 console.log(a, b)
4 // <Buffer 61 62 63> <Buffer 64 64 64>
5 
6 let c = Buffer.concat([a, b]);
7 console.log(c);
8 // <Buffer 61 62 63 64 64 64>

Buffer数据操作:

 1 // 查找
 2 let a=new Buffer('abccc-=-dddder-=-qwerqwer');
 3 console.log(a.indexOf('-=-'));
 4 
 5 // 截取
 6 let b=new Buffer('abccc-=-dddder-=-qwerqwer');
 7 console.log(b.slice(0, 5).toString());
 8 
 9 // 切分  -->  目前buffer自带的操作中没有可以直接用的split
10 let c=new Buffer('abccc-=-dddder-=-qwerqwer');
11 
12 Buffer.prototype.split=Buffer.prototype.split||function (c){        // 如果buffer有split就用buffer自带的split,没有就用下面的函数
13     let arr=[];
14 
15     let cur=0;
16     let n=0;
17     while((n=this.indexOf(c, cur))!==-1){
18         arr.push(this.slice(cur, n));
19         cur=n+c.length;
20     }
21 
22     arr.push(this.slice(cur));
23 
24     return arr;
25 };
26 
27 let arr=c.split('-=-');
28 console.log(arr);
29 console.log(arr.map(buffer=>buffer.toString()));

 

 

3.C++ Addons - 用C语言/C++写插件给node用

 

 

4.多进程

理论上JavaScript是单进程单线程的,可以通过以下模块实现多进程:

  • Child Processes
  • Cluster
  • Process 

注:node中没有多线程的直接实现(为了考虑安全性、应用性)

(1)进程与线程

  • 进程:进程拥有独立的执行空间和存储空间
  • 线程:同一个进程内的所有线程共享一套空间、代码
  • 多进程:成本高(慢)、安全(进程间隔离)、进程间通信麻烦、写代码简单、PHP、node
  • 多线程:成本低(快)、不安全(线程间共享)、线程间通信简单、写代码复杂、Java、C
  • 多进程:慢、简单、安全
  • 多线程:快、复杂、脆弱

 

(2)进程之间的通信方法

  • 管道
  • 共享内存
  • socket

 

(3)详细用法

详细用法见:https://www.cnblogs.com/wyb666/p/9704056.html

 


5.Crypto——散列、签名
crypto模块提供了md5、sha算法,主要用来进行加密(实质上是散列)、签名

普通加密:

1 const crypto = require('crypto')
2 
3 let obj = crypto.createHash('sha1')
4 // 或者用md5加密:
5 // let obj = crypto.createHash('md5')
6 
7 obj.update('123456')
8 
9 console.log(obj.digest('hex'))  # 以16进制输出数据

二次加密并加盐:

 1 const crypto = require('crypto')
 2 
 3 function md5(str){
 4     let obj = crypto.createHash('md5')
 5     obj.update(str)
 6 
 7     return obj.digest('hex')
 8 }
 9 
10 // 二级加密并加盐
11 console.log(md5(md5('123456') + 'asdfghjklzxcvbnm,./' ))

 


6.http

  • HTTP/HTTPS
  • HTTP/2

下面是用http模块搭建简单服务器的大致过程:

最简单的服务器:

 1 const http = require("http")
 2 
 3 let server = http.createServer(function(req, res){
 4     // 路由处理
 5     switch(req.url){
 6         case '/aaa':
 7           res.write('abc');
 8           break;
 9         case '/bbb':
10           res.write('dddd');
11           break;
12         case '/1.html':
13           res.write('<html><head></head><body>sdfasfasf</body></html>');
14           break;
15     }
16     res.end()
17 });
18 
19 // 监听
20 server.listen(8080)

 

前后端代码分离的服务器(前端代码存在www文件夹下):

 1 const http=require('http');
 2 const fs=require('fs');
 3 
 4 let server=http.createServer(function(req, res){
 5   fs.readFile(`www${req.url}`, function(err, data){
 6     if(err){
 7       res.write('404');     // 404页面
 8     }else{
 9       res.write(data);
10     }
11     res.end();
12   });
13 });
14 
15 server.listen(8080);

注意:fs.readFile是一个异步操作,必须将res.end()放在readFile内,如果放在readFile外面会导致以下错误:

这个错误是因为程序不会等readFile执行完就会执行后面的end,因此要将end放在readFile内才会在读完文件后执行end

 

 

7.OS和Path

  • OS:系统相关
  • Path:处理路径
 1 const os=require('os');
 2 const path=require('path');
 3 
 4 // 输出CPU信息:
 5 console.log(os.cpus());
 6 
 7 // 路径相关:
 8 let str='/var/local/www/aaa/1.png';
 9 //dirname  -> 文件夹路径
10 //basename -> 文件名
11 //extname  -> 拓展名
12 console.log(path.dirname(str));   // /var/local/www/aaa
13 console.log(path.basename(str));  // 1.png
14 console.log(path.extname(str));   // .png

 


8.Events事件队列

(1)机制原理

Nodejs的大部分核心API都是基于异步事件驱动设计的,所有可以分发事件的对象都是EventEmitter类的实例。

大家知道,由于nodejs是单线程运行的,所以nodejs需要借助事件轮询,不断去查询事件队列中的事件消息,然后执行该事件对应的回调函数,有点类似windows的消息映射机制

 

(2)使用实例

 1 const Event = require("events").EventEmitter
 2 
 3 let ev = new Event()
 4 
 5 // 1、监听(接受)
 6 ev.on('msg', function(a, b, c){
 7     console.log('收到了msg事件', a, b, c);
 8 })
 9 
10 // 2、派发(发送)
11 ev.emit('msg', 12, 5, 98)

 

(3)注意

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类

这样做的原因有以下两点:
  • 具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法
  • JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系
Events(事件)模块是Node.js的核心,许多其他模块用它来围绕着事件架构功能。由于Node.js运行在单一的线程中,任何同步代码都是阻塞的,所以如果有长时间运行的代码的话事件循环便会被阻塞。为了有效地使用Node.js编写代码,必须仔细思考自己的变成风格并遵循一些简单的规则

 

 

9.Query Strings和URL
(1)Query Strings

Query Strings:查询字符串,url中的?之后的字符串即为Query Strings

比如www.xxx.com/find?s=k&wd=123中的查询字符串就是 s=k&wd=123

querystring实例:

1 const querystring = require("querystring")
2 
3 // url: www.xxx.com/find?s=k&wd=123
4 let obj = querystring.parse("s=k&wd=123")
5 
6 console.log(obj)
7 // 解析结果: { s: 'k', wd: '123' }

 

(2)URL

url模块和querystring模块不同之处:url模块解析整个url,而querystring只能解析url中问号之后的字符串

实例:

 1 const url = require("url")
 2 
 3 let obj = url.parse("www.xxx.com/find?s=k&wd=123")
 4 
 5 console.log(obj)
 6 /*
 7 输出结果:
 8 Url {
 9   protocol: null,
10   slashes: null,
11   auth: null,
12   host: null,
13   port: null,
14   hostname: null,
15   hash: null,
16   search: '?s=k&wd=123',
17   query: 's=k&wd=123',
18   pathname: 'www.xxx.com/find',
19   path: 'www.xxx.com/find?s=k&wd=123',
20   href: 'www.xxx.com/find?s=k&wd=123'
21 }
22 */

注意:也可以像下面一样指定将query也一并解析

 1 const url = require("url")
 2 
 3 let obj = url.parse("www.xxx.com/find?s=k&wd=123", true)
 4 
 5 console.log(obj)
 6 /*
 7 输出结果:
 8 Url {
 9   protocol: null,
10   slashes: null,
11   auth: null,
12   host: null,
13   port: null,
14   hostname: null,
15   hash: null,
16   search: '?s=k&wd=123',
17   query: { s: 'k', wd: '123' },
18   pathname: 'www.xxx.com/find',
19   path: 'www.xxx.com/find?s=k&wd=123',
20   href: 'www.xxx.com/find?s=k&wd=123'
21 }
22 */

 

 

10.网络相关模块

  • TCP-稳定  ->   Net
  • UDP-快     ->   UDP/Datagram
  • DNS          ->   域名解析相关
  • Domain     ->   域名相关 

DNS解析实例:

 1 const dns = require("dns")
 2 
 3 dns.resolve("baidu.com", function(err, res){
 4     if(err){
 5         console.log("解析失败")
 6     } else {
 7         console.log(res)
 8     }
 9 
10 })
11 
12 // 结果: [ '220.181.57.216', '123.125.115.110' ]

 

 

11.流操作——Stream

(1)什么是流

连续数据都是流:比如说视频流、网络流、文件流、语音流

 

(2)Stream具体操作

读取写入文件:

 1 const fs = require('fs')
 2 
 3 let rs = fs.createReadStream('1.txt')       // 读取流
 4 let ws = fs.createWriteStream('2.txt')      // 写入流
 5 
 6 rs.pipe(ws)
 7 
 8 // 异常处理
 9 rs.on('error', function (error) {
10     console.log('读取失败!')
11 })
12 
13 // 读取完成 及 写入完成
14 rs.on('end', function () {
15     console.log('读取完成!')
16 })
17 
18 ws.on('finish', function () {
19     console.log('写入完成!')
20 })

 

 

12.TLS/SSL

用于加密、安全

 

 

13.ZLIB
用于压缩 - gz压缩

zlib模块使用实例:

 1 const zlib = require('zlib')
 2 const fs = require('fs')
 3 
 4 let rs = fs.createReadStream('jQuery.js')
 5 let ws = fs.createWriteStream('jQuery.js.gz')
 6 
 7 let gz = zlib.createGzip()
 8 
 9 rs.pipe(gz).pipe(ws)
10 
11 // 异常处理
12 rs.on('error', function (error) {
13     console.log('读取失败!')
14 })
15 
16 // 读取完成 及 写入完成
17 rs.on('end', function () {
18     console.log('读取完成!')
19 })
20 
21 ws.on('finish', function () {
22     console.log('写入完成!')
23 })

 

 

 

三、node.js数据交互

web服务器三大任务:返回文件(html、css、图片等)、数据交互(get、post)、数据库,下面的部分将围绕返回文件及数据交互展开

1.返回文件

返回文件可以使用node.js中的fs模块,实例如下:

 1 const fs = require("fs")
 2 
 3 fs.readFile('1.txt', function(err, data){
 4     if(err){
 5         console.log("有错!");
 6     } else {
 7         console.log(data);  // 二进制 Buffer -> 可以直接把这样的数据返回给前端(图片只能返回二进制,返回字符串将使图片失效)
 8         console.log(data.toString());
 9     }
10     
11 })  

 

 

2.数据交互 - get和post和文件上传

  • get数据:url里面、小于32K
  • post数据:作为body、比较大
  • file数据:form表单的处理、后端的处理

(1)设置header

  • setHeader()  -->  一般使用这种来设置header(键值对)
  • writeHeader() 
  • write()

简单设置header:

 1 const http=require('http');
 2 const fs=require('fs');
 3 
 4 let server=http.createServer(function(req, res){
 5   fs.readFile(`www${req.url}`, function(err, data){
 6     if(err){
 7         // 返回404
 8         res.writeHeader(404);           // header
 9         res.write('Not Found');         // body
10     }else{
11         res.write(data);
12     }
13     res.end();
14   });
15 });
16 
17 server.listen(8080);

 

(2)get数据处理

get表单:

1 <form action="http://localhost:8080/aaa" method="get">
2     用户:<input type="text" name="user" /><br>
3     密码:<input type="password" name="pass" /><br>
4     <input type="submit" value="提交">
5 </form>

后端node:

 1 const http=require('http')
 2 const url=require('url')
 3 
 4 let server=http.createServer(function(req, res){
 5     let {pathname, query} = url.parse(req.url, true)
 6     console.log(pathname)  // -> /xxx的形式
 7     console.log(query)     // -> { user: 'xxx', pass: 'xxx' }的形式
 8 
 9     res.end()
10 })
11 
12 server.listen(8080)

 

(3)post数据处理

post表单(注意get和post请求可以同时提交):

1 <form action="http://localhost:8080/aaa?id=12&a=55" method="post">
2     用户:<input type="text" name="user" /><br>
3     密码:<input type="password" name="pass" /><br>
4     <input type="submit" value="提交">
5 </form>

后端node:

 1 const http=require('http')
 2 const querystring=require('querystring')
 3 
 4 let server=http.createServer(function(req, res){
 5     let str=''
 6 
 7     // 有一个段到达了
 8     req.on('data', function(data){
 9         str+=data
10     })
11 
12     // 结束了
13     req.on('end', function(){
14         let post=querystring.parse(str)
15         console.log(str)
16         console.log(post)
17     })
18 
19     res.end()
20 })
21 
22 server.listen(8080)

注意:url和querystring的不同之处:

 1 // url解析整个url
 2 url.parse("www.xxx.com/aaa/bbb?a=12&b=5")
 3 url.parse("/aaa/bbb?a=12&b=5")
 4 // 另外加上true表示进一步解析query参数(不加就默认不进一步解析query):
 5 url.parse("/aaa/bbb?a=12&b=5", true) 
 6 -> 
 7 {
 8   、、、
 9   "query": {a: 12, b: 5}
10   、、、
11 }
12 
13 // querystring解析数据
14 querystring.parse("a=12&b=5")

 

(4)get和post数据一块处理

前端还是使用前面的get表单和post表单

后端node:

 1 const http=require('http');
 2 const url=require('url');
 3 const querystring=require('querystring');
 4 
 5 let server=http.createServer((req, res)=>{
 6     // GET
 7     let {pathname, query}=url.parse(req.url, true);
 8 
 9     // POST
10     let str='';
11     req.on('data', function(data){
12       str+=data;
13     });
14     req.on('end', function(){
15         let post=querystring.parse(str);
16 
17         console.log(pathname, query, post);
18     });
19 
20     res.end()
21 });
22 
23 server.listen(8080);

注意:在一个表单中可以get请求可以和post请求同时发

 

(5)文件上传

前端代码:

1 <form action="" method="post" enctype="multipart/form-data">
2     <input type="file" name="upload-file">
3     <input type="submit" value="上传文件">
4 </form>
5 
6 注意:
7     上传文件时表单中的enctype="multipart/form-data"必须要写
8     input(file)必须要有name

后端可以使用fs中的readFile和writeFile实现(读取完上传的文件后保存)

这样做有弊端:

  • 只能等到所有数据都到达了才开始处理
  • readFile先把所有数据全读到内存中,然后回调:
  • 1.极其占用内存
  • 2.资源利用极其不充分

更好的方法:使用流,实例见后面的文件上传实例

 

3.数据交互实例 - 登陆注册简单实现

(1)基本原理

 1 // GET数据 -> 在url中
 2 let {pathname, query} = url.parse(req.url, true)    // 请求的地址及?之后的参数
 3 
 4 // POST数据 -> 在body里 比较大
 5 let str = ''
 6 req.on('data', function(data){
 7     str += data    // post提交的数据
 8 })
 9 
10 req.end('end', function(err){
11     let post = querystring.parse(str)  // 解析提交的数据(字符串->对象)
12 })

 

(2)前端代码

 1 <!-- author: wyb -->
 2 <!DOCTYPE html>
 3 <html lang="en">
 4 <head>
 5     <meta charset="UTF-8">
 6     <title>登陆</title>
 7     <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
 8 </head>
 9 <body>
10 
11 用户名: <input type="text" id="user"> <br>
12 密码: <input type="password" id="pass"> <br>
13 <input type="button" value="注册" id="btn1">
14 <input type="button" value="登陆" id="btn2">
15 
16 <script>
17     /*
18         // 前后端接口:
19         用户注册:
20         /reg?user=xxx&pass=xxx
21         =>{error: 0, msg: '说明'}
22 
23         用户登陆:
24         /login?user=xxx&pass=xxx
25         =>{error: 0, msg: '说明'}
26     */
27     $(function () {
28         // 注册
29         $('#btn1').click(function () {
30             $.ajax({
31                 url: '/reg',
32                 data: {user: $('#user').val(), pass: $('#pass').val()},
33                 dataType: 'json',
34                 success(data){
35                     if(data.error){
36                         alert("错了: " + data.msg)
37                     } else {
38                         alert("注册成功")
39                     }
40                 },
41                 error(){
42                     alert("错了")
43                 }
44             })
45         })
46 
47         // 登陆
48         $('#btn2').click(function () {
49             $.ajax({
50                 url: '/login',
51                 data: {user: $('#user').val(), pass: $('#pass').val()},
52                 dataType: 'json',
53                 success(data){
54                     if(data.error){
55                         alert("错了: " + data.msg)
56                     } else {
57                         alert("登陆成功")
58                     }
59                 },
60                 error(){
61                     alert("错了")
62                 }
63             })
64         })
65     })
66 </script>
67 
68 </body>
69 </html>

 

(3)后端代码(node)

 1 const http = require('http');
 2 const url = require('url');
 3 const querystring = require('querystring');
 4 const fs = require('fs');
 5 
 6 /*
 7     // 前后端接口:
 8     用户注册:
 9     /reg?user=xxx&pass=xxx
10     =>{error: 0, msg: '说明'}
11 
12     用户登陆:
13     /login?user=xxx&pass=xxx
14     =>{error: 0, msg: '说明'}
15 */
16 
17 // users在内存中保存用户登陆信息
18 let users = {
19 //  'xxx': '123456',
20 //  'wyb': '654321'
21 };
22 
23 let server = http.createServer(function(req, res) {
24     // GET
25     let {pathname, query} = url.parse(req.url, true);
26 
27     // POST
28     let str = '';
29     req.on('data', function (data) {
30         str += data;
31     });
32 
33     req.on('end', function () {
34         let post = querystring.parse(str);
35         let {user, pass} = query
36 
37         switch (pathname) {
38             // 注册
39             case '/reg':
40                 if (!user) {
41                     res.write('{"error": 1, "msg": "user is required!"}')
42                 } else if (!pass) {
43                     res.write('{"error": 1, "msg": "pass is required!"}')
44                 } else if (!/^\w{3,32}$/.test(user)) {
45                     res.write('{"error": 1, "msg": "invalid username!"}')
46                 } else if (!/^\w{6,32}$/.test(pass)) {
47                     res.write('{"error": 1, "msg": "invalid password!"}')
48                 } else if (/^['"|]$/.test(pass)) {
49                     res.write('{"error": 1, "msg": "invalid password!"}')
50                 } else if (users[user]) {
51                     res.write('{"error": 1, "msg": "username already exists!"}')
52                 } else {
53                     users[user] = pass
54                     res.write('{"error": 0, "msg": "register success!"}')
55                 }
56 
57                 res.end()
58                 break
59             // 登陆
60             case '/login':
61                 if (!user) {
62                     res.write('{"error": 1, "msg": "user is required!"}')
63                 } else if (!pass) {
64                     res.write('{"error": 1, "msg": "pass is required!"}')
65                 } else if (!users[user]) {
66                     res.write('{"error": 1, "msg": "no this user!"}')
67                 } else if (users[user]!==pass) {
68                     res.write('{"error": 1, "msg": "username or password is incorrect!"}')
69                 } else {
70                     res.write('{"error": 0, "msg": "login success!"}')
71                 }
72 
73                 res.end()
74                 break
75             default:
76                 fs.readFile(`www${pathname}`, function (err, data) {
77                     if (err) {
78                         res.writeHead(404)
79                         res.write("Not Found!")
80                     } else {
81                         res.write(data)
82                     }
83 
84                     res.end()
85                 })
86         }
87 
88     });
89 
90 });
91 
92 server.listen(8080);

 

 

4.文件上传实例 - 用流实现

直接看这里:https://www.cnblogs.com/wyb666/p/9694481.html

 

posted @ 2018-09-12 16:15  woz333333  阅读(279)  评论(0编辑  收藏  举报