笔记 - NodeJS

第01节:概述

  ● 优势:性能高;跟前端JS配合方便;与前端技术栈更近,便于学习。

第02节:创建服务器

  ● 在NodeJS中自带有HTTP模块,可以直接使用require方法调用模块,然后使用HTTP模块新建一个服务器。例如:

const http = require('http');//调用组件并新建常量接收;
//使用组件创建服务器,服务器的参数是一个回调函数,服务器有访问便会调用回调函数,这个函数有两个参数,request是请求信息(输入),response是响应信息(输出);
var server = http.createServer(function (req, res) {
  console.log('有人访问。');//服务器每次响应都会提示;
  res.write('abc');//响应信息的输出方法;
  res.end();//结束请求的方法;
});

server.listen(8181);//服务器需要监听端口来响应请求;

  ● 请求(request)参数的属性:req.url,可以获取请求的文件地址。

第03节:文件操作

  ● 在NodeJS中的文件操作依赖的是fs(File System)模块,下面是它的读和写方法:

    1.读取方法fs.readFile(),例如:

const fs = require('fs');//导入文件处理模块;
//readFile(文件名, 回调函数);
//回调函数中的err是返回的错误信息,没有错误便返回null;data是请求到的数据,返回的是文件的二进制源码;
fs.readFile('test.txt', function (err, data) {
  if (err) {
    console.log('读取失败。')
  } else {
    console.log(data.toString());
  }
});

    2.写入方法fs.writeFile(),例如:

const fs = require('fs');
//writeFile(文件名, 内容, 回调函数);
//回调函数参数是错误信息,没有错误便返回null;
fs.writeFile('test.txt', 'test content', function (err) {
	console.log(err);
});

    3.创建服务器和文件操作结合使用示例:

const http = require('http');//导入HTTP模块;
const fs = require('fs');//导入文件操作模块;

var server = http.createServer(function (req, res) {//创建服务器;
  var fileName = './www' + req.url;//处理文件请求的URL,将URL和本地文件夹合并为文件地址;
  fs.readFile(fileName, function (err, data) {//读取文件;
    if (err) {
      res.write('404');//读取失败返回信息;
    } else {
      res.write(data);//读取成功返回文件;
    }
    res.end();//要在文件读取的函数内部结束响应;
  });
});

server.listen(8181);//监听端口;

第04节:解析GET数据

  ● 当form表单使用GET方式提交数据时,可以使用request参数的url属性获得数据。

  ● 使用Query String组件的parse方法可以方便的解析使用GET方式提交的URL地址中的数据,直接将URL中的数据返回为一个JSON对象。例如:

const querysting = require('querystring');

var json = querysting.parse('user=testname&pwd=test1234');
console.log(json);//{ user: 'testname', pwd: 'test1234' }

  ● 解析GET数据还有更便捷的方式,就是URL组件中的parse方法,它能解析URL地址中包含的所有数据,方法的第二个参数传入true会将Query String数据解析出来。例如:

const urlLib = require('url');

var obj = urlLib.parse('user=testname&pwd=test1234', true);
var json = obj.query;
console.log(json);//{ user: 'testname', pwd: 'test1234' },obj的pathname属性解析出来是请求的文件路径;

第05节:解析POST数据

  ● 使用GET方式传送数据最大32K,而POST方式可以到1G。

  ● 使用POST方式传输的数据是在消息的Content部分,而GET数据在消息的Header部分;要解析POST数据需要使用request参数的on方法的data事件(参数)。例如:

const http = require('http');

http.createServer(function (req, res) {
  var str = '';//保存接收到的数据,这里使用字符串是为了测试,不是正确用法;
  var i = 0;//保存传输次数;
  //data代表有一段数据到达,当数据体积比较大时,需要分段多次传输;
  req.on('data', function (data) {//参数data代表每次接收到的数据;
    console.log('第' + i++ + '次收到数据');//当数据体积较大时就会显示传输的次数;
    str += data;//将收到的数据拼接起来;
  });
  //end代表数据已经全部传输完成;
  req.on('end', function () {
    console.log(str);
  })
}).listen(8181);

  ● 小复习:

//导入基础模块;
const http = require('http');//HTTP协议;
const fs = require('fs');//文件系统;
const queryString = require('querystring');//QS数据解析;
const urlLib = require('url');//URL数据解析;
//创建服务器;
var server = http.createServer(function (req, res) {//传入req请求参数和res响应参数;
  var str = '';//保存请求提交的POST数据;
  req.on('data', function (data) {//当有数据传输时的操作;
    str += data;//将传输进来的POST数据进行拼接;
  });
  req.on('end', function () {//当数据请求完成时的操作;
    var obj = urlLib.parse(req.url, true);//解析URL数据,并确认解析其中包含的QS数据;
    const URL = obj.pathname;//保存需要请求的文件路径;
    const GET = obj.query;//保存请求提交的GET数据;
    const POST = queryString.parse(str);//解析POST数据并保存;
    console.log(url, GET, POST);//展示接收到的数据;
  });
  //文件处理
  var fileName = './www' + url;//拼接本地路径和解析出的请求路径;
  fs.readFile(fileName, function (err, data) {//传入err错误信息和data找到的相关数据;
    if (err) {
      res.write('404');//错误时的返回信息;
    } else {
      res.write(data);//将找到的数据返回;
    }
    res.end();//读取结束;
  });
});
//服务器监听8181端口;
server.listen(8181);

第06节:接口

  ● 接口需要在项目开始就定义好,在URL中的接口就是相应的请求地址和保存在Query String数据中的键值对。

  ● 读取文件和访问接口需要进行区分,接口量不多可以先使用条件语句处理。

  ● 新练习加小复习:

const http = require('http');
const fs = require('fs');
const queryString = require('querystring');
const urlLib = require('url');

var users = {};//模拟用户数据存储;
var server = http.createServer(function (req, res) {
  var str = '';
  req.on('data', function (data) {
    str += data;
  });
  req.on('end', function () {
    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) {//条件中GET的act属性是定义好的接口,判断它的值是哪个再进行相应的操作;
        case 'reg'://自定义的注册接口,这里需要检测诸如用户名是否存在,密码是否过于简单等条件及相应的操作;
          if (users[GET.user]) {//检查在模拟数据中是否有请求数据中的对应属性;
            res.write('{"ok": false, "msg": "用户名已存在"}');
          } else {
            users[GET.user] = GET.pass;//没有重名便将请求数据的键值存入模拟数据中;
            res.write('{"ok": true, "msg": "注册成功"}');
          }
          break;
        case 'login':
          if (users[GET.user] == null) {//检测模拟数据中是否有请求的数据;
            res.write('{"ok": false, "msg": "用户名不存在"}');
          } else if (users[GET.user] !== GET.pass) {//有数据时检测键值对是否匹配;
            res.write('{"ok": false, "msg": "用户名或密码错误"}');
          } else {//条件都通过;
            res.write('{"ok": true, "msg": "登录成功"}');
          }
          break;
        default:
          res.write('{"ok": false, "msg": "未知的请求"}');
          break;
      }
      res.end();
    } else {
      var fileName = './www' + URL;
      fs.readFile(fileName, function (err, data) {
        if (err) {
          res.write('404');
        } else {
          res.write(data);
        }
        res.end();
      });
    }
  });
});

server.listen(8181);

第07节:模块化——系统模块概览

  ● 除了之前用过的模块,常用系统模块还有:断言(Assert)模块、网络操作(Net)模块、事件(Events)模块、加密(Crypto)模块、操作系统信息(OS)模块等等。

第08节:模块化——自定义模块

  ● 模块的组成:

    1.require:请求,用来引入模块;引入自定义模块需要加路径,除非放到模块文件夹(node_modules)中;后缀名可加可不加。

    2.module:模块,可以通过module.exports属性批量输出数据。

    3.exports:输出,其实它属于module,如果要对外输出数据,必须将数据做为exports的属性,这样可以控制数据输出。

    4.模拟示例:

exports.a = 12;

exports.b = function () {
  var res = 0;
  for (var i = 0; i < arguments.length; i++) {
    res += arguments[i];
  }
  return res;
};

module.exports = {
  x: 55,
  y: 66,
  z: function (a, b) {
    return a - b;
  }
};

  ● 介绍npm:NodeJS Package Manager(NodeJS包管理器),它的作用是:

    1.统一下载途径;

    2.自动下载依赖;

    3.发布自己的模块:需要注册npm账号。

第09节:框架Express——基本用法

  ● 非破坏式框架,保留原生的功能,但提供了增强的功能。

  ● 提供了三种方法处理相应的请求,主要使用use方法,因为它能通吃数据。示例:

const express = require('express');

var server = express();//不用创建服务器,直接使用express方法即可;
//第一参数是请求路径或接口,第二参数是回调函数;
server.use('/a.html', function (req, res) {//回调函数中的两个参数不是原生的对象,除了原生的功能外,还有新加的特性;
  res.send('test');//虽与write功能类似,但功能更多,例如可以解析JSON数据;
  res.end();
});
//专门接收GET数据的方法;
//server.get('/', function (req, res) {});
//专门接收POST数据的方法;
//server.post('/', function (req, res) {});
server.listen(8181);

  ● 静态文件操作:使用插件express-static在学习中发现这个的插件并不能正常的工作,是因为在NodeJS4.x以上的版本,内置了唯一的中间件,成为一个方法。

  ● 基本用法实例:

const express = require('express');//导入模块

var server = express();//创建服务器;
server.listen(8181);//监听端口;
//模拟数据
var users = {
  "test": 123456,
  "zhangsan": 654321,
  "lisi": 123123
};
//处理GET数据;
server.get('/login', function (req, res) {
  //提取GET数据,并将对应数据保存;
  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: '登陆成功'})
    }
  }
});
//使用自带的static方法托管静态文件;
server.use(express.static('./www'));

第10节:框架Express——中间件

  ● 所谓“中间件”其实就是插件,上一节中提到的被内置的express-static就是一个中间件,在这一节中讲的是一个过渡性中间件——body-parser。

  ● 在express中的req.query解析的是GET数据,使用body-parser插件后,可以用req.body解析POST数据,要使用req.body之前必须先在use中使用urlencoded方法。例如:

//导入等略;
//先将bodyParser对象的urlencoded方法传入,这个方法接收一个JSON参数,里面有两个属性;
server.use(bodyParser.urlencoded({//没有这步解析body数据,就不会有下面的body方法;
  extended: false,//扩展模式,使用很少;
  limit:2 * 1024 * 1024//体积限制,默认100K,这里所示是2MB;
}));
//解析数据;
server.use('/', function (req, res) {
  console.log(req.query);//解析GET数据;
  console.log(req.body);//解析POST数据;
});

  ● 上面的例子中可以看出,所谓“中间件”,就是在使用它之前要use一下,这样才能在随后的代码中使用中间件所提供的方法。

  ● 使用express的use方法可以实现链式操作,在回调函数参数中有个next,也是一个方法,它会像req和res一样传递给下一个use方法,但链式操作必须都能请求同一个地址。例如:

//导入等略;
server.use('/', function (req, res, next) {//这里的use不给传路径参数也可以传给下面的use方法;
  console.log('1');
  next();
});
server.use('/', function (req, res) {//这里传next但后面没有操作便无意义;
  console.log('2');
});
//控制台依次输出1和2;

  ● 如果use方法不指定路径,便会对所有请求做出响应。

  ● 尝试自己写一个类似body-parser具有解析POST数据功能的基础中间件,例如:

    1.中间件:

const queryString = require('querystring');

module.exports = 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();//将控制权交给下一个方法;
    });
  };
};

    2.引入使用:

//创建等略;
const myBodyParser = require('./my-body-parser');//导入模块;

server.use('/', myBodyParser());//使用模块;
server.use('/', function (req, res) {
  console.log(req.body);
});

第11节:框架Express——cookie和session

  ● 由于HTTP是无状态的,无法识别用户是否为同一个,所以利用cookie在浏览器保存一些数据,每次请求都会进行识别,来解决此问题;但是cookie有安全隐患,且容量很小(4KB)。

  ● 与cookie作用类似,session也是用来保存数据的,但它被保存在服务端,安全性相对高,容量也很大;但session是基于cookie(Session ID)实现的,无法独立存在。

  ● 向浏览器发送cookie使用res参数的cookie方法。示例:

//参数1和2是键值对,参数3是一个JSON数据,path代表cookie的归属目录,当请求这个目录才会写入或读取,maxAge是过期时间,单位是毫秒,例子中设置了30天;
res.cookie('user', 'testName', {path: '/testPath', maxAge: 30 * 24 * 3600 * 1000});

  ● 为了防止cookie被篡改,有些时候需要给cookie签名,这需要先设置一个签名值,然后再给cookie签名。例如:

//这里的secret就是签名值,值是一个可自定义的字符串,cookie签名必须有签名值才可以;
req.secret = 'testCode';
//参数中JSON对象中的signed就是签名选项,true就代表要签名;
res.cookie('user', 'test', {path: '/aaa', maxAge: 30 * 24 * 3600 * 1000, signed: true});

  ● 如果要读取浏览器请求所带的cookie数据,可以使用cookie-parser中间件,之后可以在req的cookies属性读取到cookie数据;使用这个中间件还可以解析签名后的cookie数据。例如:

//导入等略;
server.use(cookieParser('testCode'));//使用中间件时将设定的签名值传入中间件参数,这里传入了签名值时,后面就不需要再使用req.secret属性了;
server.use('/', function (req, res) {
  console.log('有签名', req.signedCookies);//使用signedCookies解析有签名的cookie数据;
  console.log('无签名', req.cookies);//使用cookies解析无签名的cookie数据;
});

  ● 由于cookie的容量很小,所以要省着用,能短则短,而签名会导致cookie体积增大,并不是每个cookie都需要签名。

  ● 删除cookie可以将cookie的过期时间直接设置为0,还可以使用res.clearCookie方法,要将准备删除的cookie名和路径传入。例如:

res.clearCookie('user', {path: '/testPath'});

  ● 要给cookie加密可以使用cookie-encrypter中间件,由于给cookie加密的情况不多,所以这个中间件不常用。

  ● 解析session数据可以使用cookie-session中间件;为了防止session劫持,使用cookie-session之前需要为它添加密钥,不然没法启动它。方法如下:

//导入等略;
server.use(cookieParser());//使用cookie-session之前要先使用cookie-parser中间件;
server.use(cookieSession({//参数是以JSON的形式传入的;
  name: 'sess',//默认是‘session’,如果有这个参数便为设置好的名称;
  keys: ['aaa', 'bbb', 'ccc'],//密钥是一个数组,每个数组元素都是一个密钥,系统会循环使用,个数越多安全性越高;
  maxAge: 3600 * 1000//过期时间,单位是毫秒;
}));
//也可以使用比较高(wei)级(suo)的方式;
var arr = [];
for (var i = 0; i < 100000; i++) {
  arr.push('sig_' + Math.random());
}
//之后将这个数组作为keys的值;

  ● 给session中间件设置好密钥后,就可以直接使用req.session来进行相应的操作了,由于session属于要写入服务器的数据,所以是在req参数中。例如使用session记录访问次数:

//导入等略;
server.use('/', function (req, res) {
  if (req.session['count'] === null || req.session['count'] === undefined) {
    req.session['count'] = 1;
  } else {
    req.session['count']++;
  }
  console.log(req.session['count']);//每次刷新输出两次是因为favicon的缘故;
  res.send('ok');
});

  ● 要删除session可以使用delete关键字,直接delete req.session即可。

第12节:模板引擎——初识Jade

  ● 主流的有Jade和Ejs等,Jade属于破坏式(侵入式)的,不能和普通HTML+CSS共存,强依赖型;Ejs比较温和,属于非侵入式,不破坏原有的HTML+CSS,弱依赖型。

  ● 使用Jade(现已改名为Pug)的基本语法:

    1.根据缩进规定层级的;属性使用括号包裹,多属性使用逗号分隔;内容是与代码用一个空格隔开,内容也可以嵌套。

    2.使用Jade默认会将内容压缩,没有空格和换行,这在开发阶段不方便,可以在渲染方法中加入参数,参数为JSON格式,其中添加preptty:true就可以美化编译出的代码。

    3.设置style和class属性时,都可以使用普通方法写入,style属性还可以使用JSON格式写入,而class属性可以使用数组形式写入,代码复杂时可以处理数组来改变样式。

    4.在Jade中,可以在标签名后面使用&attribute来识别JSON格式的属性值。

    5.渲染页面的方法由jade.render('String')和jade.renderFile('File Name', Arguments),一般使用后者。

第13节:模板引擎——还是Jade

  ● 在Jade中可以使用自定义标签,所有自定义标签都是双标签。

  ● 在Jade中原样输出内容,可以在需要的内容前面加入竖线符号“ | ”,如果这样的内容很多,可以直接在放内容的标签后面直接加“ . ”符号。

  ● 类似原样输出,内容前面加“ - ”就识别为代码,如果要编译代码可以使用“ #{} ”,在大括号中放入内码,等价于在标签后面加“ = ”号。

  ● 为了防止注入式攻击,一般在内容里的HTML标签会被转义为实体字符,如要原样输出,要在包含内容的标签等号前加非转义符号“ ! ”,就可以正常输出HTML标签。

  ● 使用include可以将外部文件的内容引入并写进要编译的jade文件中。

  ● 在Jade中使用switch和JS中不同,是使用case加when的关键字,例如:

doctype
  html
    head
    body
      -var a=3;
      case a
        when 0
          div abc
        when 1
          div 123
        when 2
          div abc123
        default
          div nonono

  ● 使用Jade的小练习:

    1.JS部分,文件名为main.js:

const jade = require('jade');//引入Jade引擎;
const fs = require('fs');//引入文件操作模块;

var str = jade.renderFile('./views/index.jade', {pretty: true});//将目标jade文件渲染,并美化编译后的代码;
fs.writeFile('./build/index.html', str, function (err) {//渲染后的文件存放位置及文件名和包含要渲染文件的变量,最后的回调函数处理错误信息;
  if (err) {
    console.log('编译失败!');
  } else {
    console.log('成功!')
  }
});

    2.Jade部分,文件名为index.jade:

doctype//文档头是一个类似关键字的值;
html
  head//依靠缩进来进行层级的区分;
    meta(charset="utf-8")//属性写在括号中;
    title 测试页面//在标签名后面加个空格后直接写内容;
    style(type="text/css").//在.号后面的都是代码原格式;
      div {width: 100px; height: 200px; background-color: red; text-align: center; line-height: 200px; float: left; margin: 10px auto;}
      div.last {clear: left;}
  body
    -var a = 0;//如果-号后面都是代码,那么不需要每行前面添加;
    while a < 12//在Jade里,JS代码中的括号可以不写;
      if a % 4 === 0 && a !== 0
        div.last=a++
      else
        div=a++

    3.HTML部分,与输出文件名设定相同,index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>测试页面</title>
    <style type="text/css">
      div {width: 100px; height: 200px; background-color: red; text-align: center; line-height: 200px; float: left; margin: 10px auto;}
      div.last {clear: left;}
    </style>
  </head>
  <body>
    <div>0</div>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div class="last">4</div>
    <div>5</div>
    <div>6</div>
    <div>7</div>
    <div class="last">8</div>
    <div>9</div>
    <div>10</div>
    <div>11</div>
  </body>
</html>

第14节:模板引擎——初识Ejs

  ● 使用ejs的方法与jade相仿,下载组件后导入,使用renderFile方法来渲染指定的ejs文件;ejs文件的格式与普通的HTML代码相似,每行代码要用<%%>来包起来。

  ● 在ejs中使用的JS代码和原生的JS代码是一样的,包括循环语句或判断语句。

  ● 在ejs中,使用=号是转义输出,使用-号是不转义输出。例如:

<% var str = '<div></div>'; %>
<%= str %>
<!--这里会输出&lt;div&gt;&lt;/div&gt;-->
<%- str %>
<!--这里会输出<div></div>-->

  ● 在ejs中同样也可以include将指定文件的内容引入页面中,引入的时候要注意文件的位置。

第15节:期中总结

转载于:https://www.cnblogs.com/battlehawk/p/7732301.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值