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
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作为常驻服务进程,没有不适合,只有合理的资源调度和利用才是王道)
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);