express系列(2)Node基础
模块的使用
在大多数编程语言中,我们都会对代码进行拆分,然后在使用的时候将这些文件引入其中。
例如,C 和 C++ 中的 include,Python 的 import ,Ruby 和 PHP 中的 require。而另外一些语言,如 C# 是在编译时完成跨文件引用的。
很长一段时间内,JavaScript 官方并不支持模块机制。后来,Node 通过实现名为 CommonJS 的标准模块,完美的解决了模块导入问题。
模块系统部分主要有三大主要内容:内置模块的引入,第三方模块引入,个人私有模块引入。下面,将会对这些内容逐一介绍。
引入内置模块
Node 已经内置了很多实用模块,例如,文件系统模块 fs,工具函数模块 util。
在 Node 编写的 Web 应用中,最常见的任务当属 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 命令,你就会看到协议名,域名、查询条件。
保存变量名和模块名一致只是一个统一风格增加可读性的宽松约定,而不是什么强制规范。
使用 npm 和 package.json 引入第三方模块
Node 的内置模块远远不能满足日常开发需要,所以引入第三方模块是一个必须要掌握的技能。
npm init, 首先,我们需要了解 package.json 文件。所有的 Node 项目都单独存放在一个文件夹中,而项目如果使用了第三方模块,那么其中必定存在一个名为 package.json 的文件。package.json 中的内容非常的简单,一般其中定义了项目名称、版本号、作者,已经项目的外部依赖项。
{ "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 就行了,你无需担心。
实现私有模块
前面都是介绍如何使用他人开发好的模块,接下来你将会学到如何去开发一个私有模块。假设现在需要随机返回 0 ~ 100 之间的整数。在不引入其他模块的情况下,代码大致如下:
var MAX = 100; function randomInteger() { return Math.floor( (Math.random() * MAX) ); }
var MAX = 100; function randomInteger() { return Math.floor( (Math.random() * MAX) ); } module.exports = randomInteger;
接下来我们就来使用一下这个新模块。在 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:异步的世界
在第一章中,我用 “烤松饼” 的例子简单的介绍了 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"); });
下面我们就来做个测试。这里,我们在上面代码的结束加上一段,那么会发生什么事情呢?
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 应用中这意味着相同的时间可以处理更多的请求。
用 Node 构建 Web 服务:http 模块
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 解析的代码大致如下:
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 框架出现的重要原因。
提点
本文主要内容:
- Node 的安装
- 模块系统的使用
- package.json 文件的介绍
- 通过 package.json 安装第三放模块依赖项
- Node 中的异步编程概念。
- 简单 HTTP 服务应用的创建。