目录
一、认识nodejs
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境(引自百度百科)。
我们都知道,浏览器是客户端的JavaScript运行环境,相对的,nodejs就是服务端的JavaScript运行环境。
我们说nodejs是服务端的运行环境,并不意味着nodejs只能用于为web提供后台服务。nodejs实际上是让JavaScript可以运行于浏览器之外,并且为JavaScript提供了本地文件系统访问、数据库访问,以及作为web后台服务所必需的http等接口,使JavaScript真正成为一门可以用于web后端的编程语言。JavaScript在nodejs的环境下运行,就像Java在虚拟机环境下运行一样。
因此,说nodejs是一门编程语言,这显然是错误的。nodejs本身只是为JavaScript提供了一个可以不在浏览器下运行的环境。既然是运行环境,那么它必定包含能够解析这门语言的编译器或解释器,nodejs的作者选择了性能卓越的JavaScript引擎:V8(由C++编写,主要用于Chromium和Chrome)。
仅仅有V8还是不够的,因为它只能解析JavaScript本身,无法为其提供访问操作系统其他服务的编程接口。要让JavaScript能作为服务端语言,至少要为其提供访问文件系统、数据库等的能力,并且提供http服务,nodejs的作者Ryan Dahl以及技术社区为此使用C++开发了一系列的模块,如fs(文件系统模块)、http(http服务模块)、mysql(mysql数据库模块)、oracledb(Oracle模块)等,这里的fs和http模块是核心模块,内置于nodejs核心运行环境内,无需额外安装。有了这些模块,你可以用JavaScript访问与操作系统相关的更多底层服务。
所以说,客户端的JavaScript等于ECMAScript + DOM + BOM,而服务端的JavaScript等于ECMAScript + fs + mysql + oracledb + http + os(操作系统模块) + net(TCP模块) + …
有了这些模块的支持,JavaScript才算完全脱离了浏览器环境,具备了与操作系统交互的能力,因此它可以作为web后端语言使用。
既然在nodejs环境下,JavaScript已经可以与操作系统交互了,那它就不限于作为web后端语言了。此时的JavaScript还常用于编写工具,比如常见的webpack、vue-cli、create-react-app等,甚至可以用于编写桌面应用,如微软旗下著名的基于electron的开发者工具vscode。
二、nodejs基本概念简介
1. nodejs与npm
npm是nodejs“附赠”的包管理工具,当然它也是nodejs的作者推荐的包管理工具(nodejs现已交由npm的作者来维护)。当你安装完nodejs,npm也就安装完成了。
npm的主要作用是管理第三方包(如一个vue组件、一个react组件,或者打包工具webpack以及各种脚手架等,都称为一个包)。使用npm命令,你可以从npm服务器上下载别人上传的第三方包或命令行到本地使用,也可以上传自己的本地包供别人使用。
比如你想下载webpack,可以使用以下命令:
npm install webpack -g
install webpack表示要安装webpack。-g参数表示这个包要安装到nodejs的根目录下,这样可以作为全局包使用。想要卸载已安装的包,可以使用uninstall命令。
npm访问的默认服务器地址是https://registry.npmjs.org/
,上传下载第三方包的目标服务器都是它。如果使用淘宝镜像cnpm,访问的是淘宝的服务器,一般来说,该服务器每10分钟与官方服务器同步一次。
2. nodejs交互式解释器(REPL)
不同于Java,JavaScript是一门脚本语言,它的执行不需要经过编译,因此可以一行一行地执行。基于这个特点,nodejs为JavaScript提供了一个交互式解释器,用于在命令行即时编写和执行JavaScript。
安装完nodejs后,打开命令行,输入node
,敲击回车,即可进入交互式执行环境:
出现一个大于号,表示已经进入交互式环境,接下来你可以输入JavaScript语句,敲击回车即可执行:
如果需要连续输入多行代码,可以按shift + 回车
,解释器会换行但暂不执行上一语句。
使用nodejs提供的交互式解释器,可以方便地编写并运行少量JavaScript代码。如果代码量稍大,可以将代码保存在一个js文件中,然后用node命令执行该js文件,效果与在交互式环境下连续输入这些代码是一致的。
3. nodejs异步编程
nodejs使用了一个事件驱动、非阻塞式 I/O 的模型。它的非阻塞I/O是借助事件循环实现的。
我们都知道,JavaScript是一门单线程语言。如果同步地执行所有的任务,诸如网络请求、数据库查询这样的操作将耗费大量的时间,导致程序性能极低。所以和在浏览器中一样,nodejs也采用事件循环机制,通过回调函数来解决这个问题(图片来自菜鸟教程)。
相比于客户端JavaScript,服务端JavaScript具有更大的优势。因为客户端浏览器类型不一,版本也有所差异,对JavaScript版本的支持性差异很大,给前端页面开发带来了很大的限制。但在服务端,nodejs版本由我们决定,所以我们可以更放心地使用高版本语法,比如Generator函数、async函数等,来实现异步编程。
此外,nodejs还提供了child_process
模块,为JavaScript提供多进程支持。它比浏览器端的webworker
更加强大,因为webworker
只是一种多线程实现,各个线程之间不具备通信能力,而由child_process
创建的进程是可以通信的。
4. 全局对象global
我们知道,在浏览器环境下,全局对象是window。而在nodejs环境下,全局对象是global。
同window一样,引用global的属性也可以省略global。global的常见属性包括:
- __filename:当前脚本的绝对路径,含文件名
- __dirname:当前脚本所在的路径,不含文件名
- setTimeout()、clearTimeout()、setInterval()、clearInetrval():用法与浏览器环境下一致
- console:控制台对象,用法与浏览器下类似。不过浏览器环境下的控制台由浏览器提供,而nodejs环境的控制台通常指shell终端(即命令行窗口)。
- process:nodejs进程状态对象。为process绑定事件监听,可以在nodejs的各个生命周期下执行操作。与nodejs交互的过程中,我们经常会访问这个对象。
三、nodejs的几个常用模块
nodejs有很多模块,这里我们只对其中几个最为常用的进行一些简介,以对nodejs有个大致的了解。
1. fs模块
fs模块是nodejs提供的文件系统模块,提供一组类似于UNIX系统的文件操作API。引入fs模块非常简单:
let fs = require("fs");
得到了fs模块后,我们就可以像下面这样读取文件系统的一个文件:
// 异步读取
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("异步读取: " + data.toString());
});
还可以以同步的方式来读取文件:
// 同步读取
var data = fs.readFileSync('input.txt');
console.log("同步读取: " + data.toString());
实际上在Chrome浏览器中,JavaScript也具备读取本地文件内容的能力,但是向文件系统中写入内容是严格禁止的,因为这会给客户端安全带来极大的安全隐患。而在nodejs环境下,由于是服务端,不存在这类安全问题,因此nodejs提供的fs模块允许向文件系统写入内容:
var fs = require("fs");
fs.writeFile('input.txt', '新内容', function(err) {
if (err) {
return console.error(err);
}
console.log("数据写入成功!");
});
这会向当前文件夹下的input.txt写入“新内容”这几个字符,如果该文件不存在,将先创建它再写入。
nodejs提供的文件系统操作接口使得它可以为各种脚手架、打包工具等提供支持。比如vue或者react的脚手架,实际上就是利用nodejs提供的fs模块,根据输入的参数,复制事先写好的模板文件到目标文件夹下。而打包工具,如webpack,则是对项目文件夹下的文件进行整合压缩和输出,也离不开nodejs提供的文件系统操作API。
2. web模块
web模块由若干子模块组成,如http模块、https模块、ws模块、net模块(提供TCP连接)、url模块等,为web服务提供支持。有了这些模块的支持,nodejs才能为JavaScript提供完整的服务端环境。此外,在nodejs环境下,JavaScript还可以实现一个简单的客户端。
我们以一个简单的例子来介绍web模块(例子来自菜鸟教程):
首先我们在当前文件夹下新增一个index.html作为服务端资源,它的内容很简单:
index.html
<!DOCTYPE html>
<html>
<body>
Hello World!
</body>
</html>
然后在同级目录下新建server.js:
server.js
var http = require('http'); // http模块
var fs = require('fs'); // 文件系统模块
var url = require('url'); // url解析模块
// 创建服务器
http.createServer( function (request, response) {
// 解析请求,包括文件名
var pathname = url.parse(request.url).pathname;
// 输出请求的文件名
console.log("Request for " + pathname + " received.");
// 从文件系统中读取请求的文件内容
fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
console.log(err);
// HTTP 状态码: 404 : NOT FOUND
// Content Type: text/html
response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 状态码: 200 : OK
// Content Type: text/html
response.writeHead(200, {'Content-Type': 'text/html'});
// 响应文件内容
response.write(data.toString());
}
// 发送响应数据
response.end();
});
}).listen(8080);
console.log('Server running at http://127.0.0.1:8080/');
然后创建一个客户端文件client.js:
client.js
var http = require('http');
// 用于请求的选项
var options = {
host: 'localhost',
port: '8080',
path: '/index.html'
};
// 处理响应的回调函数
var callback = function(response){
// 不断更新数据
var body = '';
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
// 数据接收完成
console.log(body);
});
}
// 向服务端发送请求
var req = http.request(options, callback);
req.end();
打开命令行,输入以下命令:
这样我们的服务就运行起来了,运行网址是http://127.0.0.1:8080
(如果你在浏览器地址栏输入http://127.0.0.1:8080/index.html
,就可以看到本地的index.html页面)。
再打开一个命令行,然后输入:
可以看到,即使没有浏览器,我们也可以通过nodejs提供的http模块,从http://127.0.0.1:8080
这个网址下请求到index.html的内容。只是此时的html文件由于没有浏览器的解析,只是作为一串普通字符串返回的。基于这一点,你可以在nodejs环境下,用JavaScript写一个爬虫程序,主要过程就是不断请求网页,然后对取到的网页字符串进行分析。
不同于Java,nodejs自行封装了http服务,因此JavaScript运行在nodejs环境下时不需要其他的web容器(如Tomcat、Jetty等)。
3. 数据库模块
作为服务端环境,访问数据库是必不可少的。nodejs为各大数据库提供对应的模块,以使JavaScript可以访问这些数据库。常见的模块如MySQL数据库的mysql模块,Oracle数据库的oracledb模块、MongoDB的mongodb模块等。这些模块不是nodejs的核心模块,需要从npm下载安装。
下面我们以Oracle数据库连接来介绍nodejs的oracledb模块的简单用法(需要先安装oracledb:npm install oracledb):
var oracledb = require('oracledb');
const param = {
user: xxx,
password: xxx,
connectString: xxx(数据库地址及实例)
};
const sql = "select * from USER where id=:1";
let userId = "123"; // 待查询的用户id
oracledb.getConnection(param, function(err, connection){
if (err) {
console.error(err.message);
return;
}
console.log("数据库连接成功");
// 数组的userId元素将替换sql语句中的:1
connection.execute(sql, [userId], function(err, res){
if (err) {
console.error(err.message);
}
console.log(JSON.stringify(res.rows[0]);
})
})
使用不同的数据库时,安装对应的模块即可。
总结
nodejs是JavaScript的服务端环境,它使得JavaScript不仅可以用于编写web后端,还可以用于编写各种工具、脚手架等。
nodejs用很多框架,如express、koa、eggjs等,在实际使用nodejs搭建web服务时,常常直接使用这些框架。nodejs的作者推荐使用中间件模式来开发web后台服务,每个中间件就是一个处理函数,对请求进行一步处理,类似于Java的过滤器。这样开发者就可以专注于业务逻辑的开发。
nodejs社区非常活跃,有大量可用的模块,框架也是数不胜数,这里只是nodejs初识篇,等以后在工作中深入使用后再进行更细致的了解。