Node从入门到入土

194 阅读7分钟

1.Node是啥?

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

  • node既不是语言也不是框架,而是js的运行平台

  • 构建于Chrome的V8引擎之上,解析执行JavaScript

2.Node和浏览器中用到的js有何不同?

浏览器中的js: ECMAscript(基本语法、function、Array...)、DOM、BOM     

作用:开发Web页面交互效果、操作DOM、BOM

Node中的js:

  • ECMAscript

  • 没有DOM、BOM

  • node为JavaScript提供了一些服务器级别的API:

    • 例如文件的读写

    • 网络服务的构建

    • 网络通信

    • http服务器

3.Node的组成(架构)

  • Node.js 标准库,由 Javascript编写的,就是使用过程中直接能调用的 API。

  • Node bindings,这一层是 Javascript与底层 C/C++ 能够沟通的关键,前者通过 bindings 调用后者,相互交换数据

  • 最底层是支撑 Node.js 运行的关键,由 C/C++ 实现。

 V8: Node.js 为什么使用的是 Javascript的关键,它为 Javascript提供了在非浏览器端运行的环境,它的高效是 Node.js 之所以高效的原因之一。
Libuv:为 Node.js 提供了跨平台,线程池,事件池,异步 I/O 等能力,是 Node.js 如此强大的关键。 
C-ares:提供了异步处理 DNS 相关的能力。
http_parser、OpenSSL、zlib 等:提供包括 http 解析、SSL、数据压缩等其他的能力。

果然,真正的大佬还是c ....

4.Node的执行机制

  • 每个Node.js进程只有一个主线程在执行程序代码,形成一个执行栈execution context stack)。

  • 主线程之外,还维护了一个"事件队列"(Event queue)。当用户的网络请求或者其它的异步操作到来时,node都会把它放到Event Queue之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。

  • 主线程代码执行完毕完成后,然后通过Event Loop,也就是事件循环机制,开始到Event Queue的开头取出第一个事件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,然后第三个,第四个。主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交EventLoop处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池

  • 主线程不断重复上面的第三步,看似单线程,实则事件循环机制将所有的阻塞操作交给了内部的线程池来处理,而主线程本身就是在不断的往返调度。

5.Node的特点

非阻塞I/O模型(异步I/O)、事件驱动、单线程、跨平台

  • 异步、非阻塞

在我的理解中非阻塞和异步实际上是两个不同的概念,同步/异步关注的是消息通信机制,即在调用发出得到结果前是否会直接返回;非阻塞&阻塞强调的是程序等待结果时的**状态,**即调用结果返回前,线程/程序是否会挂起或处于等待状态。

具体的例子网上有很多,可以自己找找体会一下其中的差异

一个很常见的栗子: ajax请求

$.post('/url', {title: 'Node.js'}, function (data) {
  console.log('收到响应');
});
console.log('发送Ajax结束');

收到响应前后续代码已经被执行了,不会等待响应结果且不影响后续执行,这就是典型的异步非阻塞模型

  • 事件驱动

事件之间各自独立,只关注事务点、低耦合、轻量级

Node中的实现就是异步回调函数

//创建一个Web服务器
//事件驱动 & 回调函数
var http = require('http');
var querystring = require('querystring');

// 监听服务器的request事件
http.createServer(function (req, res) {
  var postData = '';
  req.setEncoding('utf8');
  // 监听请求的data事件
  req.on('data', function (chunk) {
    postData += chunk;
  });
  // 监听请求的end事件
  req.on('end', function () {
    res.end(postData);
  });
}).listen(8080);
console.log('服务器启动完成');
  • 单线程

这里的单线程实际上只是主线程,具体的机制可以看下上文Node的运行机制

至于为什么是单线程,因为js这门语言本质是单线程的(当然,worker之类的多线程的实现方式就不多深究了,Node本身也有类似的实现)

相比多线程,单线程存在程序稳定性(一点出错,全盘崩溃),执行效率低等劣势,但不需要考虑线程转换、状态同步等问题。

6. Node的应用场景

  • RESTful API (最理想的应用场景)
  • 充分利用事件循环机制,资源占用少,擅长I/O密集型应用
  • Node不适合CPU密集型应用 ? (可以通过C/C++扩展、child_process作为常驻服务进程,没有不适合,只有合理的资源调度和利用才是王道)
总体来讲,Node适合运用在高并发、I/O密集、少量业务逻辑的场景。

7. Node的模块系统

额,总之就是node也是站在了commonjs模块规范这个巨人的肩上完成了自身的模块系统的演变 (commonjs规范无非就是引用和模块定义,就不展开讲了)

node的模块规范就几个重要的点(需要深入了解的可以看看朴灵大佬的《深入浅出Node.js》):

  • 优先从缓存加载

  • 路径分析和文件定位 (核心模块、路径形式的文件模块、自定义模块)

  • 模块编译 (.js 、 .json 、 .node、 ...)

    .js : 在编译的过程中,Node对获取的JavaScript文件内容进行了头尾包装。

    (function (exports, require, module, __filename, __dirname) { var math = require('math'); exports.add = function (x, y) { return x + y; }; });

      .json: JSON.parse() => 赋值给exports

      .node: process.dlopen()方法进行加载和执行

核心模块

  • http模块: 处理客户端网络请求

  • fs模块: 处理文件(上传、返回到浏览器)

  • url模块: 处理客户端请求过来的url

  • path模块: 处理文件与目录的路径

  • querystring模块: 处理客户端通过get/post请求传递过来的参数

  • global模块: 全局模块,不需要引用 (_dirname 、 _filename、require() 、exports...)

      events(触发器)、os(操作系统)、dns(域名服务器)....

8.Api示例

简单贴上几个用的比较多的,具体的可以看下官网

  • 文件操作

    var fs = require('fs');

    fs.readFile('./a.txt', 'utf8', function(err, data){ if(err){ console.log('文件读取失败'); }else{ console.log(data); } })

    fs.writeFile('./a.txt','我是写入的信息', 'utf8', function(err){ if(err){ console.log('文件写入失败'); }else{ console.log('文件写入成功'); } })

    fs.stat('./index.js',(err, data) => { if(err) { console.log('文件不存在'); return; }; console.log(data.isFile()); console.log(data.isDirectory()); console.log(data); });

  • 路径处理

    const path = require('path');

    console.log(path.normalize('D://test'));

    console.log(path.resolve('./'));

    console.log(path.extname('D:/test/abc.txt'));

    console.log(path.basename('D:/test/abc.txt'));

    console.log(path.parse('D:/test/abc.txt'));

  • 创建web服务器

    // 加载http核心模块 var http = require('http');

    // 使用http.createServer()创建一个web服务器 var server = http.createServer();

    //监听request事件 server.on('request', function(){ console.log('收到客户的请求了') })

    // 绑定端口号,启动服务 server.listen(3000, function(){ console.log('runing...') })

  • 如何响应 ? 根据不同请求路径响应不同数据? 

    var http = require('http'); var fs = require('fs');

    http.createServer((req, res) => { if(req.url !== '/favicon.ico'){ console.log('请求地址:' + req.url) if (req.url == '/') { res.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8' }); res.end('响应数据') } else if (req.url == '/html') { res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' }); res.end('

    html

    ') } else if (req.url == '/json') { var json = { code: '0', status: 'success', payload: { list: ['vue','node','javaScript'] } }; res.end(JSON.stringify(json)) } else if (req.url == '/file') { fs.readFile('./test.html', 'utf8', function(err,data){ if(err){ res.writeHead(500, { 'Content-Type': 'text/html;charset=utf-8' }); res.end('文件资源请求失败'); }else{ res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' }); res.end(data); } }) } else if (req.url == '/img') { fs.readFile('./test.jpg', function(err,data){ if(err){ res.writeHead(500, { 'Content-Type': 'text/html;charset=utf-8' }); res.end('文件资源请求失败'); }else{ res.writeHead(200, { 'Content-Type': 'image/jpeg' }); res.end(data); } }) } else { res.writeHead(404, { 'Content-Type': 'text/html;charset=utf-8' }); res.end('404') } } }).listen(3000);

  • Get/Post 请求

    var http = require('http'); var url = require('url'); http.createServer(function(req, res){ res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'}); console.log(url.parse(req.url)); var params = url.parse(req.url).query; res.write("Name:" + params.name); res.write("\n"); res.write("Url:" + params.url); res.end(); }).listen(3000);

9. Node操作数据库

  • node操作mysql (操作其他数据库的可以自行网上找找)
  • npm install myql  (安装个依赖包先)

还有一些环境的集成软件,把数据库还有数据表备好就可以愉快的蹂躏数据了~

var mysql = require('mysql');
// var http = require('http'); 

var connection = mysql.createConnection({     
  host: 'localhost',       
  user: 'root',              
  password: '123456',       
  port: '3306',                   
  database: 'nodetest'
});

connection.connect((err) => {
    if (err) {
        console.log('[query] - :' + err);
        return;
    }
    console.log('[connection connect]  succeed!');
});

var sql = 'SELECT * FROM user';
//查
connection.query(sql, function (err, result) {
    if(err){
      console.log('[SELECT ERROR] - ',err.message);
      return;
    }

   console.log('--------------------------SELECT----------------------------');
   console.log(result);
   console.log('------------------------------------------------------------\n\n');  
});

// var addSql = 'INSERT INTO user(name,age) VALUES(?,?)';
// var addSqlParams = ['啊哈哈', 24];
// //增
// connection.query(addSql, addSqlParams, function (err, result) {
//     if(err){
//      console.log('[INSERT ERROR] - ', err.message);
//      return;
//     }        

//    console.log('--------------------------INSERT----------------------------');     
//    console.log(result);        
//    console.log('-----------------------------------------------------------------\n\n');  
// });

connection.end(function (err) {
    if (err) {
        return;
    }
    console.log('[connection end] succeed!');
});

// http.createServer((req, res) => {
// 	console.log('请求地址:' + req.url)
// 	if (req.url == '/') {
// 		res.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8' });
// 		var sql = 'SELECT * FROM user';
// 		connection.query(sql, function (err, result) {
// 		    if(err){
// 		      console.log('[SELECT ERROR] - ',err.message);
// 		      return;
// 		    }
// 			let data = JSON.stringify(result)//将数据转换为json格式
// 	        res.end(data)
//             connection.end(function (err) {
// 			    if (err) {
// 			        return;
// 			    }
// 			    console.log('[connection end] succeed!');
// 			});
// 		});
// 	}
// }).listen(3000);

10. 以上,因为懒0.0,所以很多都没有展开多写(笔记里面复制粘贴:)),大佬们见谅~