Node 模块原理与用法详解

积少成多 2020-06-14

本文实例讲述了Node 模块原理与用法。分享给大家供大家参考,具体如下:

简介

V8引擎本身就是用于Chrome浏览器的JS解释部分,但是Ryan Dahl,把V8搬到服务器,用于做服务器的软件。
Node是一个专注于实现高性能Web服务器优化的专家,在遇到V8而诞生的项目

  • 没有历史包袱,没有同步I/O。不会出现一个同步I/O导致事件循环性能急剧降低的情况。
  • V8性能足够好,远远比Python,Ruby等其它脚本语言的引擎快。
  • JavaScript语言的闭包特性非常方便,比C中的回调函数好用。

Node可以让JavaScript运行在服务器端的平台开发,它让JavaScript的触角延伸到了服务器端,可以与PHP,JSP,Python,Ruby等语言实现后端开发。

但Node似乎有点不同:

  • Node不是一种独立的语言,与PHP,JSP,Python,Perl,Ruby的“即使语言,也是平台”不同,Node使用的是JavaScript进行编程,运行在JavaScript引擎上(V8)
  • 与PHP,JSP等相比(PHP,JSP,.net都需要运行在服务器程序上,Apache,Naginx,Tomcat,IIS),Node跳过了Apcahe,Naginx,IIS等HTTP服务器,它自己不用建设在任何服务器任何之上。Node的设计理念与经典架构(LAMP = Linux + Apache + MySQL + PHP) 有着很大的不同,可以提供强大的伸缩能力。
  • Node没有Web容器。
  • Node是花最小的硬件成本,追求更高的并发,更高的处理性能。

Node特点

所谓特点,就是Node如果解决服务器高性能瓶颈问题。
JavaScript有什么特点的时候,会立即想到 单线程事件驱动面向对象。但是JavaScript精髓 觉得是 this闭包作用域链函数。才使得这门语言魅力无穷。

单线程

在Java,PHP,或者.net 等服务器端语言中,会为每一个用户端连接创建一个新的线程。而每个线程需要耗费大约2MB内存。理论上,一个8GB内存的服务器可以同时连接的最大用户数4000个左右。要让Web应用程序支持更多的用户,就需要增加服务器的数量,而Web应用程序的硬件成本就上升了。
Node不为每个用户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,并通过非阻塞I/O,事件驱动机制,让Node程序宏观上也是并行的。Node中,一个8GB内存的服务器,可以同时处理超过4万用户的连接。
单线程好处:操作系统完全不再有线程创建,销毁的时间开销。
单线程坏处:就是一个用户造成了线程的奔溃,整个服务都奔溃了,其它人的服务也就奔溃了。

Node 模块原理与用法详解

Node 模块原理与用法详解

单线程也能够造成宏观上的“并发”。

非阻塞I/O

非阻塞I/O non-blocking I/O

例子:访问数据库取得数据的时候,需要一段时间。
在传统的单线程处理机制中,在执行了访问数据库代码之后,整个线程都将暂停下来,等待数据库返回结果,才能执行后面的代码。也就是说I/O阻塞了代码的执行,极大降低了程序执行的效率

Node采用了非阻塞型I/O机制,因此在执行了访问数据库的代码之后,将立即转而执行后面的代码,把数据库返回的结果的处理代码放在回调函数中,从而提高了程序的执行效率。
当某个I/O执行完毕时,将以时间的形式通知执行I/O操作的线程,线程执行了这个事件的回调函数。为了处理异步I/O,线程必须有事件循环,不断的检查是否有未处理的时间。依次予以处理。
阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞模式下,一个线程永远在执行计算操作,这个线程的CPU核心利用率永远是100%。 有一种类似 : 与其多人工作,但是好多人闲着,倒不如一个人工作,往死里干活。

Node 模块原理与用法详解

事件驱动
事件驱动 event-driven

在Node中,客户端请求建立连接,提交数据等行为,会触发相应的时间。在Node中,在一个ie时时刻,只能执行一个事件回调函数,但是在执行一个事件回调函数的中途,可以转而处理其它事件(比如:有新用户连接),然后返回继续执行原事件的回调函数。这种处理机制,称为:"事件环"机制。

Node底层是C++(V8也是C++) 编写。底层代码中,近半数都用户事件队列,回调函数队列的构建。用事件驱动来完成服务器的任务调度。用一个线程,担负起了处理非常多的任务。

Node 模块原理与用法详解

单线程,减少内存开销,操作系统的内存换页。
如某一个任务,执行了,但是被I/O阻塞了,所以这个县城就阻塞了。非阻塞I/O,不会傻等I/O语句结束,而会执行后面的语句。利用事件驱动,不管是新用户的请求,还是老用户的I/O完成,都将以事件方式加入事件环中,等待调度。

Node所有的I/O都是异步的,回调函数嵌套回调函数。

Node是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。
Node的每个API都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发。
Node基本上所有的事件机制都是用设计模式中的观察者模式实现的。
Node单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数。

模块

moduel

Node中,以模块为单位划分所有功能,并且提供一个完整的模块加载机制,可以将应用程序话费为各个不同的部分。

Node中,一个JavaScript文件中定义的变量,函数,都只在这个文件内部有效果。

侠义的说,每一个JavaScript文件都是一个模块,而多个JavaScript文件之间可以相互require,共同实现一个功能,整体外对,又称之为广义上的模块

好处:

  • 减少重复代码量,增加可读性。
  • 方便进行代码规划。
  • 方面使用第三方模块。

当需要从JS文件外部引用到这些变量,函数时,必须使用exprots对象,或者module.exprots进行暴露。使用者需要使用require(); 函数引入这个JS文件。

function People( name,sex,age ){
  this.name = name;
  this.sex = sex;
  this.age = age;
}

People.prototype = {
  sayHello: function(){
    console.log(this.name+this.sex+this.age);
  }
};

// 暴露
module.exports = People;

// 使用
var People = require('./People.js');

var p1 = new People('zf','nv','23');

p1.sayHello();

一个JavaScript文件,可以向外exprots无数个变量,函数,对象,但是require(); 的时候,仅仅需要 载入一次JS文件即可。 所以,无形之后,会增加一个顶层命名空间。

// 变量
// 需要变量引用 使用 
exports.a = 10;
// 直接需要变量值使用.
module.exports = name;

// 对象
module.exports = {
 name1: 123,
 name2: 456
}
// 暴露结果: { name1: 123, name2: 456 }

exports.msg = {
 name1: 1,
 name2: 2
}
// 暴露结果 : { msg: { name1: 1, name2: 2 } }



// 函数
exports.showMsg = function () {
}

// 暴露结果 : { showMsg: [Function] }
// 在 引用结果 需要 通过 变量 引用对象 执行
// var msg = require();
// msg.showMsg();


module.exports = function () {
}
// 暴露结果 [Function]
// 引入文件的 变量 直接执行

模板引擎

数据绑定,成为一个完整的HTML字符串。
Node中使用的模板:ejs 和 jade

后台模板引擎:

<ul>
  <% for(var i = 0 ; i < news.length ; i++){ %>
   <li><%= news[i] %></li>
  <% } %>
</ul>

// 模板中需要的数据
var dictionary = {
 a:6,
 news : ["xixi","haha"]
};

HTTP模块

主要类

Class: http.Server
var server = http.createServer();

server就是http.Server类的实例。

常用的方法有:

server.listen(port, [hostname], [backlog], [callback])
Class: http.ServerResponse
var server = http.createServer(function(req,res){ });

res就是 http.ServerResponse类的实例。

Class: http.IncomingMessage

``
var server = http.createServer(function(req,res){ });
``
req就是http.IncomingMessage类的实例。

server对象

可以使用on语法监听某个事件。

var http = require('http');
var server = http.createServer();

server.on('request',function ( req,res ) {
 res.setHeader('Content-type','text/html;charset=utf8');
 if ( req.url == '/' ){
  res.end('index');
 } else {
  res.end('404');
 }
});

server.listen(8080,'localhost');

req对象

每次上行请求头对象

req.headers //HTTP上行请求头

Node 模块原理与用法详解

req.httpVersion // http请求的版本。现在基本上都是1.1  
req.method  //  “GET”、”POST”就是请求的类型  
req.url // 用户请求的网址

res对象

每次下行响应对象

res.end() // 每次都要有这个语句,表示这次的发送已经结束
  // 参数必须是string、buffer(图片、文件)。

res.write()  // 就是写HTTP下行请求的body

res.setHeader()     // 设置返回的报文头部

res.statusCode = 404;  // 设置状态码

res.writeHead()     // 和res.setHeader差不多
res.writeHead(288 , {"Content-Type":"text/plain"});

url模块

作用内置模块,解析url,解析地址。 分析和解析 URL 的工具

url.parse()

url.parse()就是用来解析网址,接收一个字符串,返回一个JSON:

var obj = url.parse("http://localhost:8080/a/b/c/1.html?name=ting&age=21");

Node 模块原理与用法详解

url.parse方法第二个参数如果是true,那么返回的对象中的query就是json

query: { xingming: 'xiaoming', age: '12' }

querystring模块

querystring模块是专门用来解析GET请求的查询字符串的。

console.log( qs.parse('name=ting&age=21&hobby=run&hobby=sing') );
// 返回:{ name: 'ting', age: '21', hobby: [ 'run', 'sing' ] }

path模块

处理和转换文件路径的工具集,专门处理路径

path.basename()  返回路径中的文件名
path.dirname()  返回路径中的文件夹名
path.extname()  返回路径的拓展名

console.log( path.basename('/xixi/haha/a.html') ); //a.html
console.log( path.extname('/xixi/haha/a.html') ); //.html
console.log( path.dirname('/xixi/haha/a.html') ); ///xixi/haha

fs模块

文件处理模块,可以读文件,也可以写文件

fs.readFile();      //读取文件内容  
fs.readDir();      //读取文件夹名
fs.appendFile();    //追加文件
fs.writeFile();     //写入文件(覆盖原有的)
fs.rename();      //修改文件名

自定义模块

每一个js文件中可以看成是一个小小的模块
require()谁,就会执行谁。就相当于调用一个函数。A require B, 先执行B全部语句,然后执行A的剩余语句。

require('./test/a.js');

每个js文件就是一个闭包,声明的函数、变量只在这个js文件内部有定义。
A require了 B , 那么B里面的所有路径都要按照A的路径写。

如果需要使用到其它文件中的变量,使用exports暴露出去。

exports.*** = ***;
testA .js
var a = 100;
exports.a = a;

主文件

var testA = requrie('./testA.js');  
console.log( testA.a );

暴露唯一的接口,module.exports ,一般使用到构造函数。

如果只有写文件载入,会去默认文件夹下:node_modules 寻找是否有当前需要载入的文件

require('test.js');

也可以直接省略路径、省略文件名,只写文件夹名

require('aa');

实际上引用的是node_moduels文件夹里面的aa文件夹里面的index.js文件。
一般第三方模块,都放入node_modules文件夹中。

package.json

配置信息:

{
 "name": "my_package",  //项目名字
 "version": "1.0.0",  //版本号
 "main": "index.js",  //入口文件
 "keywords": [],    //关键词,就是搜索什么npm上能够显示你
 "author": "ag_dubs",  //作者
 "license": "ISC",   //版权协议
 "repository": {       //代码托管仓库,这个会自动生成一个连接
  "type": "git",
  "url": "https://github.com/ashleygwilliams/my_package.git"
 },
 "bugs": {        //如果发现bug应该交给谁
  "url": "https://github.com/ashleygwilliams/my_package/issues"
 },
  "dependencies": {
  "underscore": "*",
 "date-format" : "0.0.2"
 },
  "homepage": "https://github.com/ashleygwilliams/my_package"  //个人网站
}

最重要的信息是:依赖

{
 "dependencies": { 
  "underscore": "*",
 "date-format" : "^0.0.2"
 }
}

formidable

处理POST请求

// formidable 语法
var form = new formidable.IncomingForm();

// 设置上传路径
form.uploadDir = "./uploads";
form.parse(req, function(err, fields, files) {
 // fields是普通域,就是普通的文本框、单选按钮、复选按钮、textarea都存在这个对象里面
 // files是上传的文件信息
 var newname = df('yyyyMMddhhmmssSSS', new Date());
 
 fs.rename(files.touxiang.path , "./uploads/" + newname + ".jpg",function(err){
   if(err){
     res.end("error");
     return ;
   }
 });
 res.end("ok");
});

爬虫初级

需要的npm包:

  1. express
  2. request (后端发送请求的模块)
  3. cheerio (像前端一样操作拉取回来的数据)

爬虫以及Robots协议介绍:

  1. 爬虫,是中自动获取网页内容的程序。是搜索引擎的重要组成部分,因此搜索引擎优化很大程度上就是针对爬虫而做出的优化。
  2. robots.txt是一个文本文件,robots.txt是一个协议,不是一个命令。robots.txt是爬虫要查看的第一个文件。robots.txt文件告诉爬虫在服务器上什么文件是可以被查看的,搜索机器人就会按照该文件中的内容来确定访问的范围。

Node 模块原理与用法详解

var express = require('express');
var app = express();
var cheerio = require('cheerio');

app.get('/', function(req, res){

  var request = require('request');

  request('https://linxingzhang.com', function(err, response, body) {
    if (!err && response.statusCode == 200) {
      $ = cheerio.load(body); // 和jquery的$('body') 一样

      res.json({
        panel: $('#link-panel li').length
      });
    }
  });

});

app.listen(3000);

使用supervisor启动

> supervisor start app.js

常用npm包

模块名 链接地址 简介
async async 异步操作管理
bl bl 二进制数据解析
bluebird bluebird 异步操作管理
browserify browserify 发布浏览器可用的包
bunyan bunyan 日志(logging)管理
chai chai 断言
chalk chalk 命令行彩色输出
co co 异步流程管理
colors colors 命令行彩色输出
commander commander 命令行工具
debug debug Debug输出器
dockerode dockerode Docker管理
duplexify duplexify Stream流操作工具
event-stream event-stream Stream流操作工具
express express Server服务器框架
hapi hapi Server服务器框架
koa koa Server服务器框架
glob glob 文件名匹配
grunt grunt 构建工具
gulp gulp 构建工具
hyperquest hyperquest 轻量级HTTP客户端
istanbul istanbul 测试用例覆盖率分析
JSONStream JSONStream Stream流管理工具
levelup levelup LevelDB
lodash lodash 函数式编程工具
log4js log4js 日志(logging)管理工具
minimatch minimatch 文件名匹配
minimist minimist 命令行操作
mocha mocha 单元测试
moment moment 日期时间输出
mongodb mongodb MongoDB
mysql mysql MySQL
nconf nconf 配置工具
needle needle 轻量级HTTP客户端
node-fetch node-fetch Fetch API
nodemailer nodemailer Email客户端
passport passport 登录和认证
pg pg Postgres
pump pump Stream流管理工具
redis redis Redis
request request HTTP客户端
restify restify REST API搭建
socket.io socket.io WebSocket实时通信
split2 split2 Stream流管理工具
tape tape 单元测试
through2 through2 Stream流管理工具
underscore underscore 函数式编程工具
ws ws Websockets
xml2js xml2js XML转换为JavaScript
http-server http-server 命令行的HTTP服务器
nrm nrm 更改NPM下载源
node-inspector node-inspector Node调试
supervisor supervisor 检测Node进程的服务
nodemon nodemon 在文件有变化之后会自动重启服务

希望本文所述对大家node.js程序设计有所帮助。

相关推荐