在上一篇文章中,咱们简单的介绍了 Node.js 。了解到它基于 JavaScript、天生异步、拥有大量的第三方类库。本文将会在以前的基础上,对 Node.js 进行更深刻的介绍。其中主要内容包括:html
在此以前,我假设你已经掌握了 JavaScript 基础知识而且熟悉一些基本的命令行操做。另外,不要臆想经过这一章就全面掌握 Node。可是若是你有心的话,能够去阅读 Node.js 实战。前端
JavaScript 世界的一大特色就是它选择性很是多,Node 的安装也不例外。node
能够在官方下载页面找到各类版本的源代码和安装包文件。建议你使用与本身操做系统对应的安装包进行安装。固然,你也可用使用 apt-get、Homebrew 等包管理器进行安装,若是你系统有的话。具体详见官方的包管理工具的安装指南。git
若是你使用的是 Mac 或者 Linux 的话,那么我极力推荐你使用 NVM 来安装。Window 系统上的对应程序是 NVMW。这些版本管理工具,让你能够在不一样版本间进行自由切换。例如,你能够在尝试新版本的特性时,同时在系统中保留一份稳定版。另外,NVM 无需系统管理权限同时卸载也很是容易。而安装过程也只需在终端执行一行命令。程序员
如今,请在你系统中安装好 Node。github
安装完成后,先动手写个 "Hello World" 来检验一些。在新建的 helloworld.js 中加入一下代码:web
console.log("Hello, World!");复制代码
代码中主要就是使用 console.log 来打印字符串 "Hello,world!",相信对于前端程序员来讲并不会感到陌生。下面咱们使用 node helloworld.js 运行代码。若是一切正常的话,会出现以下输出:正则表达式
在大多数编程语言中,咱们都会对代码进行拆分,而后在使用的时候将这些文件引入其中。例如,C 和 C++ 中的 include,Python 的 import ,Ruby 和 PHP 中的 require。而另一些语言,如 C# 是在编译时完成跨文件引用的。npm
很长一段时间内,JavaScript 官方并不支持模块机制。因此社区中有人就编写了 RequireJS 这种工具来解决依赖项导入的问题。可是,大多数时候仍是经过 \ 标签来进行文件导入。而Node 经过实现名为 CommonJS 的标准模块,完美的解决了模块导入问题。编程
模块系统部分主要有三大主要内容:内置模块的引入,第三方模块引入,我的私有模块引入。下面,将会对这些内容逐一介绍。
Node 已经内置了不少实用模块,例如,文件系统模块 fs,工具函数模块 util。
在 Node 编写的 Web 应用中,最多见的任务当属 URL 解析了。浏览器经过特定的 URL 来请求服务器上对应的资源。例如,访问主页、访问关于页面 的网络请求。这些 URL 都以字符串的形式存在,咱们须要对其进行解析而后获取更多的信息。这里咱们经过对 URL 进行解析来介绍如何引入内置模块。
内置的 url 模块中暴露的方法很少,不过其中有一个 parse 函数很是有用。它能从 URL 字符串中提取到相似域名和路径等有益信息。
这里咱们使用 require 来实现模块导入,该命令与以前提到的 Include、Import 的做用一致。经过将模块名做为参数,该命令就能成功的返回对应的模块。大多数状况下,该返回的对象是一个 object 对象,但有时也可能会是字符串、数字、或者函数。下面是引入改模块的示例代码:
var url = require("url");
var parsedURL = url.parse("http://www.example.com/profile?name=barry");
console.log(parsedURL.protocol); // "http:"
console.log(parsedURL.host); // "www.example.com"
console.log(parsedURL.query); // "name=barry复制代码
在上面的代码中,经过 require("url") 返回一个模块对象,而后就能够像使用其余对象同样调用对象的方法。将这段代码保存到 url-test.js 中并使运行 node url-test.js 命令,你就会看到协议名,域名、查询条件。
另外,绝大多数时候咱们在引入模块的时候会用一个同名的变量来接受返回的模块对象。例如,上面就使用 url 来介绍 require("url") 的返回值。固然,你彻底能够不遵循上面的规则。若是你想的话,你也能够这么干:
var theURLModule = require("url");
var parsedURL = theURLModule.parse("http://www.example.com/profile?name=barry");复制代码
保存变量名和模块名一致只是一个统一风格增长可读性的宽松约定,而不是什么强制规范。
Node 的内置模块远远不能知足平常开发须要,因此引入第三方模块是一个必需要掌握的技能。
首先,咱们须要了解 package.json 文件。全部的 Node 项目都单独存放在一个文件夹中,而项目若是使用了第三方模块,那么其中一定存在一个名为 package.json 的文件。package.json 中的内容很是的简单,通常其中定义了项目名称、版本号、做者,已经项目的外部依赖项。
在新建的 Node 工程文件夹中,将下面的内容复制到 package.json 中。
{
"name": "my-fun-project",
"author": "Evan Hahn",
"private": true,
"version": "0.2.0",
"dependencies": {}
}复制代码
其实,在进行 Node 安装时实际上还安装了另外一个程序:npm 。一般 npm 都被称为 Node 包管理器,而这也是它最大的特点。假设,如今须要在应用中导入一个小型的标准模版系统 Mustache。它能将模版字符串转化为真正的字符串,请看代码:
// Returns "Hello, Nicholas Cage!"
Mustache.render("Hello, {{first}} {{last}}!", {
first: "Nicholas",
last: "Cage"
});
// Returns "Hello, Sheryl Sandberg!"
Mustache.render("Hello, {{first}} {{last}}!", {
first: "Sheryl",
last: "Sandberg"
});复制代码
如今,假设你想经过 Mustache 模块来编写一个简单的 Node 应用来欢迎 Nicolas Cage。
首先,在工程文件夹的根目录里运行 npm install mustache --save 。该命令会新建一个 node_modules 文件夹并将 Mustache 保存到文件夹下。 --save 参数将会把该模块添加到 pakage.json 文件中。此时 pakage.json 文件夹大体以下,其中 Mustache 会使用最新的版本。
{
"name": "my-fun-project",
"author": "Evan Hahn",
"private": true,
"version": "0.2.0",
"dependencies": {
"mustache": "^2.0.0" #A
}
}复制代码
若是你没有使用 --save 选项的话,虽然也会建立 node_modules 文件夹将把 Mustache 模块保存到同名子目录下,可是 pakage.json 将不会发生任何变化。这里之因此将这些依赖关系保存到 package.json 是为了方便其余开发者在获得工程后直接使用 npm install 完成全部依赖项的安装。另外一个缘由是 Node 项目在进行代码管理时一般都会忽略 node_modules 文件夹而只保留 package.json。
安装完成后接下来就是使用了:
var Mustache = require("mustache");
var result = Mustache.render("Hi, {{first}} {{last}}!", {
first: "Nicolas",
last: "Cage"
});
console.log(result);复制代码
保存代码到 mustache-test.js 中并执行 node mustache-test.js 命令。而后你将会看见 Hi,Nicolas Cage! 。
就是这样简单,这些依赖项安装完成后,你能够像使用内置模块同样进行调用。node_modules 中模块引入的工做直接交给 Node 就好了,你无需担忧。
固然你能够手动添加工程依赖项,而且你还能够指定依赖项的版本。
npm init
除了安装依赖项以外,npm 还能完成其余任务。例如,自动生成 package.json 而不是经过手动编辑的方式。在一个新工程的文件夹中能够经过 npm init 来配置工程名、做者、版本等信息,而后 npm 就会自定生成对应的 package.json 文件。这种自动化过程能够节约开发者的时间。
前面都是介绍如何使用他人开发好的模块,接下来你将会学到如何去开发一个私有模块。假设如今须要随机返回 0 ~ 100 之间的整数。在不引入其余模块的状况下,代码大体以下:
var MAX = 100;
function randomInteger() {
return Math.floor( (Math.random() * MAX) );
}复制代码
这可能与你在浏览器环境下代码差很少,并无什么特别之处。可是在 Node 中,咱们还须要暴露一个变量给外部使用。这样当其余程序在经过 require 进行引入的时候就能得到该变量。此例中,咱们暴露函数 randomInteger 并将代码保存到 random-integer.js 文件中。
var MAX = 100;
function randomInteger() {
return Math.floor( (Math.random() * MAX) );
}
module.exports = randomInteger;复制代码
最后一行代码对于 Node 初学者来讲可能感受有点陌生。每一个模块只能暴露一个变量,并且必须经过 module.exports 设置。本例中只暴露了一个函数变量,因此 MAX 就做为模块私有变量没法被其余文件所访问。
module.exports 能够暴露任何变量,虽然本例中是一个函数,可是一般都会是一个对象。固然,你能够暴露字符串或者数组。
接下来咱们就来使用一下这个新模块。在 random-integer.js 同一目录下,新建一个 print-three-random-integers.js 并复制下面的代码:
var randomInt = require("./random-integer"); #A
console.log(randomInt()); // 12
console.log(randomInt()); // 77
console.log(randomInt()); // 8复制代码
除了须要经过点语法指定相对路径以外,其他部分与前面几乎一摸同样。经过 node print-three-random-integers.js 命令,咱们能够检查程序的运行效果。不出意外的话,将会有三个 0 ~ 100 之间的随机数会被打印出来。
若是你尝试运行 node random-integer.js 的话,你还发现并无任何事情发生。虽然,咱们暴露了模块中的函数,可是改函数并不会执行更不会打印任何输出。
注意,这里只涉及了私有模块在工程中的使用。若是你但愿将本身的模块发布出去供其余人使用的话,能够去个人我的站点查看相关内容。
以上部分就是 Node 模块系统的简单入门。
在第一章中,我用 “烤松饼” 的例子简单的介绍了 Node 中的异步特性。其中的关键点就是,你没法同时作两件事哪怕它们是同时发生的。虽然,在烘焙过程当中我能够健身,可是,烤箱毕竟只是个外部事物。
Node 的异步工做原理与此相似,例如,你经过浏览器请求 Node 服务器上的一张小猫图片。由于该图片资源太大,因此在进行磁盘读写的时候你能够抽身去处理其余事情。此时,这个磁盘就至关于一个外部资源,咱们能够直接处理第二个请求而无需挂起等待费时操做结束。
Express 中主要有两个外部资源:
在 Node 代码中,这些异步都是经过回调进行处理的。其工做原理和在 Web 页面发送 AJAX 请求同样。在发送请求时你会附带一个回调函数,当请求处理完成后你的回调将会被执行。
例如,如今你正在硬盘上读取文件 myfile.txt 。当读取结束后,你但愿可以打印出其中字母 X 出现的次数,代码以下:
var fs = require("fs");
var options = { encoding: "utf-8" };
fs.readFile("myfile.txt", options, function(err, data) {
if (err) {
console.error("Error reading file!");
return;
}
console.log(data.match(/x/gi).length + " letter X's");
});复制代码
下面咱们一步步解释这些代码:
首先,咱们导入 Node 自带的文件系统模块。该模块主要处理文件相关内容,其中大多数都是文件读写功能。本例使用的其中的 readFile 方法。
接下来,咱们须要设置 fs.readFile 方法中的参数,第一个是文件名,第二个就是会回调函数。而且在读取结束后执行回调函数。
在 Node 中大多数回调函数都会设置错误信息 error 做为第一个参数。正常状况下该参数等于 null ,若是出现错误则该参数会保存错误信息。虽然有时候这些错误信息并不会致使程序终止执行,可是多数情形下咱们都须要对错误作出响应,例如,抛出异常并跳出回调函数。这也是 Node 中最多见的回调实践。
最后,当一切正常时咱们使用正则表达式匹配字母 X 并打印其数量。
下面咱们就来作个测试。这里,咱们在上面代码的结束加上一段,那么会发生什么事情呢?
var fs = require("fs");
var options = { encoding: "utf-8" };
fs.readFile("myfile.txt", options, function(err, data) {
if (err) {
console.error("Error reading file!");
return;
}
console.log(data.match(/x/gi).length + " letter X's");
});
console.log("Hello World!");复制代码
异步文件读取时异步操做,因此这里先打印出来的是 " Hello world! ",而后才是异步函数中的打印操做。
这就是异步模式强大的地方。当一个外部设备在处理费时操做时,你能够继续运行其余代码。在 Web 应用中这意味着相同的时间能够处理更多的请求。
注意:若是你想了解更多 JavaScript 异步的内容的话,你能够去油管上查看这个视频。视频中的讲解同时适用于 Node 和浏览器环境。
只有理解了上面那些概念,你才能更好的掌握 Node 内置的 HTTP 模块。而该模块对 Express 框架来讲又是最重要的模块之一。Node 和 Express 可以构建 Web 服务正是依赖于这个模块中的功能。
Node 的 HTTP 模块有不少特性(好比,向其余服务器发送网络请求),不过咱们将要使用的是其中一个名为 http.createServer 的方法。该方法经过其回调函数来处理每一次的网络请求,而且进行响应。下面代码中咱们将全部的响应都设置为了 "hello world" (能够保存到 myserver.js 中)。
var http = require("http");
function requestHandler(request, response) {
console.log("In comes a request to: " + request.url);
response.end("Hello, world!");
}
var server = http.createServer(requestHandler);
server.listen(3000);复制代码
上面的代码由 4 个部分构成。
首先,咱们引入 HTTP 模块并将其保存到变量 http 中。这与以前 URL 模块的操做一致。
接着,定义了一个请求处理函数 requestHandler 。教程中的几乎全部的代码要么是请求处理函数要么是调用处理函数。该函数有两个参数,request 表示请求对象,而 response 则表示响应对象。request 中包含 URL 路径、user-agent 等信息。而经过调用 response 对象方法 Node 会将响应信息打包好并发送给请求者。
余下的代码则是指定内置的 HTTP 服务在请求是执行的处理函数以及服务监听的端口号。
对于 HTTPS 来讲,咱们则可使用自带的 HTTPS 模块。除了须要配置 SSL 证书,其他的过程都同样。若是你了解 HTTPS 的话那么后期从 HTTP 切换到 HTTPS 两分钟就能搞定。即便你不了解,也没必要太过担忧。
若是你将代码保存到 myserver.js 并执行 node myserver.js 拉起服务。那么,此时你在浏览器中访问 http://localhost:3000 ,你就会看到:
你可能也注意到了,每当你发起请求的时候终端控制台都会打印一些信息。当你尝试访问不一样 URL 时,虽然控制台打印的信息不一样可是获得的响应却都是 “Hello, world!”。控制台打印的信息相似于:
请注意上面打印的 URL 信息中并不包含 localhost:3000。虽然看起来显得不那么直观,可是反过来这也是对的。毕竟使用相对路径,咱们无需修改就能在任何电脑上部署 Node 应用。
而 URL 解析的代码大体以下:
function requestHandler(req, res) {
if (req.url === "/") {
res.end("Welcome to the homepage!");
} else if (req.url === "/about") {
res.end("Welcome to the about page!");
} else {
res.end("Error! File not found.");
}
}复制代码
全部的请求 URL 均可以在这个函数里面完成处理。这样作对于简单的应用来讲确实很是简单,可是当应用规模变大以后该函数就会变的臃肿不利于维护。这也是 Express 框架出现的重要缘由。
##总结
本文主要内容:
原文地址