node

  • 首先,Node不为每个用户开辟一个线程,所以非常极端的选择了单线程。单线程,要照顾所有的用户,那么就必须有非阻塞I/O,否则一个人的I/O就把别人、自己都阻塞了。一旦有非阻塞I/O,一个人如果I/O去了,就会放弃CPU的使用权,换成另一个人使用CPU(或者执行此人后面的语句)。所以CPU的利用率100%。第一个人I/O结束了,就要用事件来通知线程,执行回调函数。此时必须有事件环,就有一个排队调度机制。Node中有超过半数的C++代码,在搭建事件环。

  • Node.js和别的老牌3P不一样:

    1. 没有自己的语法,使用V8引擎,所以就是JS。V8引擎解析JS的,效率非常高,并且V8中很多东西都是异步的。Node就是将V8中的一些功能自己没有重写(别人做了,自己就站在巨人肩膀上),移植到了服务器上。
    2. 没有web容器,就是安装配置完成之后,没有一个根目录。

http–协议

request 请求<–输入的信息

response 响应–>输出给浏览器信息

fs模块–File Systerm文件系统

fs
异步vs同步

  • 异步–多个操作同时进行,前一个操作没完成,后面的也能开始
  • 同步–一次一个

fs.readFile(文件名, (错误err, 文件内容) => {})

writeFile(文件名,内容,(err)=>{})

数据请求

前台数据请求—form ajax jsonp

后台->一样,只是接受了http请求

前后台只通过http协议传输

请求方式:
  1. GET 数据在url中一起传输
  2. POST 数据不在url里,post数据比get数据大的多

req.url ==> /aaa?user=123123123&pass=123123123123


GET数据解析
  1. 自己动手split

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const http = require('http');
    var server = http.createServer(function (req, res) {
    var GET = {};
    if (req.url.indexOf('?')>-1) {
    var arr1 = req.url.split('?');
    var arr2 = arr1[1].split('&');
    for (var i = 0; i < arr2.length; i++) {
    var arr3 = arr2[i].split('=');
    GET[arr3[0]] = arr3[1];
    }
    }
    console.log(req.url, GET);
    res.write('123');
    res.end();

    }).listen(8080);
  2. querystring

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const http = require('http');
    const querystring = require('querystring');

    var server = http.createServer(function (req, res) {
    var GET = querystring.parse(req.url.split('?')[1]);
    console.log(req.url, GET);
    res.write('123');
    res.end();

    }).listen(8080);
  3. url

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const http = require('http');
    const urllib = require('url');

    var server = http.createServer(function (req, res) {
    var GET = urllib.parse(req.url, true).query;
    console.log(req.url, GET);
    res.write('123');
    res.end();

    }).listen(8080);
POST数据解析
1
2
3
4
5
6
7
8
9
10
11
//data有一段数据到达(发生很多次)
var str = "";
var i = 0;
req.on('data', function (data) {
console.log(`第${i++}次`);
str += data;
});
//end 数据全部到达(只发生一次)
req.on('end', function () {
console.log(str);
});
综合
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
const http=require('http');
const fs=require('fs');
const querystring=require('querystring');
const urlLib=require('url');

var server=http.createServer(function (req, res){
//GET
var obj=urlLib.parse(req.url, true);

var url=obj.pathname;
const GET=obj.query;

//POST
var str='';
req.on('data', function (data){
str+=data;
});
req.on('end', function (){
const POST=querystring.parse(str);

/*
url——要什么
GET——get数据
POST——post数据
*/

//文件请求
var file_name='./www'+url;
fs.readFile(file_name, function (err, data){
if(err){
res.write('404');
}else{
res.write(data);
}
res.end();
});
});
});

server.listen(8081);

案例:登陆注册页

访问类型分两类

  1. 对文件的访问http://localhost:8080/1.html
  2. 对接口的访问http://localhost:8080/user?act=login

  1. server.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
    const http = require('http');
    const fs = require('fs');
    const querystring = require('querystring');
    const urllib = require('url');

    var users = {};//{用户名1:密码1,用户名2:密码2,用户名3:密码3,}

    http.createServer(function (req, res) {
    //解析数据
    var str = '';
    req.on('data', function (data) {
    str += data;
    });
    req.on('end', function (data) {
    var obj = urllib.parse(req.url, true);

    const url = obj.pathname;
    const GET = obj.query;
    const POST = querystring.parse(str);

    if (url == '/user') {
    switch (GET.act) {
    case 'reg':
    //1.检查用户是否存在
    if (users[GET.user]) {
    res.write('{ "ok": false, "msg": "此用户已存在"}');
    } else {
    //2.插入user
    users[GET.user] = GET.pass;
    res.write('{ "ok": true, "msg": "注册成功"}');
    }
    break;
    case 'login':
    //1.检查用户是否存在
    if (users[GET.user] == null) {
    res.write('{ "ok": false, "msg": "此用户不存在"}');
    } else if (users[GET.user] != GET.pass) {
    //2.检查用户密码
    res.write('{ "ok": false, "msg": "用户名或密码有误"}');
    } else {
    res.write('{ "ok": true, "msg": "登陆成功"}');
    }
    break;
    default:
    res.write({
    "ok": false,
    "msg": '未知的act'
    });
    }
    res.end();
    } else {
    //读取文件
    var file_name = './www' + url;
    fs.readFile(file_name, function (err, data) {
    if (err) {
    res.write('404');
    } else {
    res.write(data);
    }
    res.end();
    });
    }
    });
    }).listen(8080);
  2. user.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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
        <script src="ajax.js"></script>
    <script>
    window.onload = function () {
    var oTxtUser = document.getElementById('user');
    var oTxtPass = document.getElementById('pass');
    var oBtnReg = document.getElementById('reg_btn');
    var oBtnLogin = document.getElementById('login_btn');
    oBtnLogin.onclick = function () {
    ajax({
    url: 'user',
    data: {act: 'login', user: oTxtUser.value, pass: oTxtPass.value},
    type:'get',
    success: function (str) {
    console.log(str);
    var json = eval('(' + str + ')');
    if (json.ok) {
    alert('登陆成功');
    } else {
    alert('登陆失败 :' + json.msg);
    }
    },
    error: function () {
    alert('通信失败')
    }
    });
    };

    oBtnReg.onclick = function () {
    ajax({
    url: '/user',
    data: {act: 'reg', user: oTxtUser.value, pass: oTxtPass.value},
    type: 'get',
    success: function (str) {
    var json = eval('(' + str + ')');
    if (json.ok) {
    alert('注册成功');
    } else {
    alert('注册失败' + json.msg);
    }
    },
    error: function () {
    alert('通信失败')
    }
    });
    }
    }
    </script>
    </head>
    <body>
    用户: <input type="text" id="user"><br>
    密码: <input type="password" id="pass"><br>
    <input type="button" value="注册" id="reg_btn"><input type="button" value="登陆" id="login_btn">
    </body>

模块化

  1. 系统模块:http querystring url
  2. 自定义模块

    • require 引入自定义模块时必须加./,可以不加.js
    • exports 单个输出
    • module.exports={};批量输出
    • 自定义模块可以统一放入node_modules里面,可以不加./
  3. 包管理器

    express

    基本用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //1.创建服务
    const express = require('express');
    var server = express();
    //3. 处理用户请求server.user('地址',function(req,res){})
    server.use('/a.html', function (req, res) {
    res.send('abc');
    res.end();
    });
    //2.监听
    server.listen(8080);

express保留了原生的功能,添加了一些方法(send),增强原有功能

  1. .get(‘/‘,function(req,res){});
  2. .post(‘/‘,function(req,res){});
  3. .use(‘/‘,function(req,res){});–get和post都可以
    express案例
  • 设计接口

    请1求: /login?user=xxx&pass=xxx

    返回: {ok:true/false,msg:’结果’}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const express = require('express');
//依赖中间件
const expressStatic = require('express-static');
var server = express();
server.listen(8080);
var users = {
'blue': '123123',
'zhangsan': '321321',
'lishi': 'qwerqwer',
};
server.get('/login', function (req, res) {
var user = req.query['user'];
var pass = req.query['pass'];
if (users[user] == null) {
res.send({ok: false, msg: '用户不存在'});
} else if (users[user] != pass) {
res.send({ok: false, msg: '密码错误'});
} else {
res.send({ok: true, msg: '登录成功'});
}
});
//如果没有get到'/login',则读取静态文件
server.use(expressStatic('./www'));
bodyParser

req.query–>GET 无需额外的中间键

req.body –>POST 需要body-parser,分两步,只能解析post数据,不能解析post文件

1
2
3
4
5
6
//要先用bodyParser解析,然后才能用,body来自bodyParser
server.use(bodyParser.urlencoded({}));
server.use('/', function (req, res) {
console.log(req.body);//post
});
//{ user: '1234', pass: '1234' }

bodyParser.urlencoded({})有两个参数

  1. extended:true //扩展模式
  2. limit: 2*1024*1024
  • bodyParser的实现原理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const querystring = require('querystring');
    module.exports = {
    aaa: function () {
    return function (req, res, next) {
    var str = '';
    req.on('data', function (data) {
    str += data;
    });
    req.on('end', function () {
    req.body = querystring.parse(str);
    next();
    });
    }
    }
    };
链式操作

当server.use(‘/‘,function(req,res,next){
next();
})链式操作需要添加next

server.use(function(){})如果不加路径,无论是哪种路径请求都可以接受

  • cookie : 在浏览器保存一些数据,每次请求都会带回来,不安全,有限(4k)
    1. 读取
      • req.signedCookies
      • req.cookies
    2. 发送
      • 指定路径:res.cookie('user', 'yangze', {path: '/aaa', maxAge: 30 * 24 * 3600 * 1000});
      • 加密:`req.secret = ‘sadfasdfwe’;
        res.cookie('user', 'yang', {signed: true});`
        
      • 删除cookieres.clearCookie('user')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const express = require('express');
const cookieParser = require('cookie-parser');
var server = express();
server.listen(8080);

server.use(cookieParser('sadfasdfwe'));
server.use('/', function (req, res) {

req.secret = 'sadfasdfwe';

//res.cookie(名字, 值, {path: '/', maxAge: 毫秒, signed: true});
res.cookie('user', 'yang', {signed: true});

//读取加密的cookie使用signedCookies
console.log(req.signedCookies);//签名版
console.log(req.cookies); //未签名版
res.send('ok');
});
  • session : 保存在服务端,安全性好,空间近乎无限,session基于cookie实现,不能独立存在,cookie中有一个session的id,服务器利用session id找到session的文件、读取、写入。。。有一个隐患:session劫持

    cookie-session

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    server.use(cookieParser());<br>
    server.use(cookieSession({
    name : 'sess',//给session起名字
    keys : ['aaa','bbb','ccc'],
    maxAge : 2 * 3600 * 1000
    }));
    server.use('/',function(req, res){
    req.session
    });
    // 删除session
    delete req.session

模板引擎

用来生成页面

  1. jade
    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
    //1.根据缩进,规定层级
    //2.属性放在()里面,逗号分隔
    //3.内容,直接在后面空格然后添加
    //4.style有两种写法,一种是普通属性写法,第二种是json格式写法
    //5.class可以用数组形式写
    //6.简写 div.box -- div#div1
    //7.div&attributes({title:'aaa',id:'div1'})
    //8.|为本,代表原样输出内容
    //9. . 代表所有下一级的内容,全部原样输出
    //10. script下使用include引入外部js文件
    //11. div #{a+b}
    //12.可以在renderFile里将style写成json格式,把class写成数组格式
    //13.可以将自己的class融入到class的数组里
    //14. 使用-var a=12;在jade中定义变量
    //15. 在jade中, span #{a} ,span=a 都是输出renderFile里的数据
    //16. 在jade中可以使用for,if,else,switch
    //17. 可以使用!=,将标签<>直接写入html
    html
    head
    style
    script
    include a.js
    script.
    window.onload = function () {
    var oBtn = document.getElementById('btn1');
    oBtn.onclick = function () {
    alert('aaa');
    };
    };
    script(src='a.js')
    |window.onload=function(){
    |var oBtn=document.getElementById('btn1');
    |oBtn.onclick=funtion(){
    | alert('aaa')
    |};
    link(href="a.css",ref="ref/stylesheet")
    body
    div(class=['aaa','left-warp','active'])
    ul
    li hello
    li
    li
    div(style={width:'200px',height:'200px',background:'red'})
    div&attributes({title:'aaa',id:'div1'})
    div 我的名字: #{name}
    div(style=json)
    div(class=arr class="active")
    div
    -var a=12;
    -var b=12;
    div 结果是: #{a+b}
    div
    -for(var i=0;i<array.length;i++)
    div=array[i]
    div
    div!=content
    div=content
    div
    -var a=19;
    -if (a%2==0)
    div(style={background:'red'}) 偶数
    -else
    div(style={background:'green'}) 奇数
    div
    -var a=1;
    case a
    when 0
    div aaa
    when 1
    div bbb
    when 2
    div ccc
    default
    |没啦


    const jade = require('jade');
    const fs = require('fs');

    var str = jade.renderFile('./view/1.jade', {
    pretty: true,
    name: "yangze",
    json: {width: "200px", height: "200px", background: "red"},
    arr: ['aaa', 'bbb', 'ccc'],
    content:"<div>123</div>"
    });
    fs.writeFile('./build/1.html', str, function (err) {
    if (err) {
    console.log('fail');
    } else {
    console.log('seccess');
    }
    });
jade案例
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
doctype
html
head
meta(charset="utf-8")
title jade测试页
style.
div {
width: 100px;
height: 100px;
background-color: #999999;
line-height: 100px;
margin-top: 10px;
text-align: center;
float: left;
}
div.last {
clear: left;
}
body
-var a=0
while a < 12
if a % 4 == 0 && a != 0
div.last #{a++}
else
div #{a++}
  1. ejs
    非侵入式
    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
    //1.<%= name %>
    //2.<%= json.arr[0].name %>
    //3.<%= 12+5 %>
    //4.<%= 'asdfasdf' %>
    //5.<% include 1.txt %>
    <!doctype html>
    <html lang="en">
    <head>
    </head>
    <body>
    <% for (var i = 0;i < json.arr.length;i++) { %>
    <div>
    用户名:
    <%= json.arr[i].user %>
    密码:
    <%= json.arr[i].pass %>
    </div>
    <% } %>
    </body>
    </html>

    const ejs = require('ejs');
    ejs.renderFile('./views/1.ejs', {
    json: {
    arr:[
    {user:'blue',pass:'12341234'},
    {user:'zhangsan',pass:'12121212'},
    {user:'xiaoming',pass:'23452345'}
    ]
    }
    }, function (err, data) {
    console.log(data);
    });

Router

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var r=express.Router();
router1.use('/user_mod', r);
router1.use('/user_reg', function (){});
http://www.xxxx.com/user/user_mod
http://www.xxxx.com/user/user_reg
http://www.xxxx.com/user/user_login

var router2=express.Router();
server.use('/news', router2);
http://www.xxxx.com/news/list
http://www.xxxx.com/news/post
http://www.xxxx.com/news/content

var router3=express.Router();
server.use('/item', router3);
http://www.xxxx.com/item/buy
http://www.xxxx.com/item/mod
http://www.xxxx.com/item/del

multer

body-parser 解析post数据—>application/x-www-form-urlencoded

multer 解析post文件—>multipart/form-data

html
1
2
3
4
<form action="http://localhost:8080" method="post" enctype="multipart/form-data">
文件:<input type="file" name="f1">
<input type="submit" value="上传">
</form>
node
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const express = require('express');
const fs = require('fs');
const static = require('express-static');
const multer = require('multer');
const pathLib = require('path');
var server = express();
var multerObj = multer({dest: './www/upload'});
server.use(multerObj.any('f1'));
server.post('/', function (req, res) {
var newName = req.files[0].path + pathLib.parse(req.files[0].originalname).ext;
fs.rename(req.files[0].path, newName, function (err) {
if (err) {
res.send('wrong');
} else {
res.send('ok');
}
})
});
server.listen(8080);

consolidate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const consolidate = require('consolidate');

//配置模板引擎
//1.哪种模板引擎 使用view engine引擎来输出html
server.set('view engine', 'html');
//2.模板文件的位置
server.set('views', '模板目录');
//3.输出什么东西
server.engine('html', consolidate.ejs);

//接受用户数据
server.use(function (req, res){
//渲染模板
res.render('模板文件', 数据);
});

MD5

1
2
3
4
5
6
7
8
const crypto = require('crypto');
module.exports = {
md5: function (str) {
var obj = crypto.createHash('md5');
obj.update(str);
return obj.digest('hex');
}
};