1.1、Node.js 和 PHP、Perl 、ASP 、JSP 一样,目的都是实现动态网页,也就是说由服务器动态生成 HTML 页面。之所以要这么做,是因为静态 HTML 的可扩展性非常有限,无法与用户有效交互。同时如果有大量相似的内容,例如产品介绍页面,那么1000个产品就要1000个静态的 HTML 页面,维护这1000个页面简直是一场灾难,因此动态生成 HTML 页面的技术应运而生。
Node.js本质上和 Perl 或 C++ 一样,都可以作为 CGI 扩展被调用,但它还可以跳过 HTTP 服务器,因为它本身就是。传统的架构中 HTTP 服务器的角色会由Apache 、Nginx、IIS 之类的软件来担任,而Node.js 不需要②。Node.js 提供了 http 模块,它是由C++ 实现的,性能可靠,可以直接应用到生产环境。下图是一个简单的架构示意图。
Node.js 和其他的语言相比的另一个显著区别,在于它的原始封装程度较低。例如 PHP 中
你可以访问 $_REQUEST 获取客户端的 POST 或 GET 请求,通常不需要直接处理 HTTP 协议。这些语言要求由HTTP 服务器来调用,因此你需要设置一个 HTTP 服务器来处理客户端的请求,HTTP 服务器通过CGI 或其他方式调用脚本语言解释器,将运行的结果传递回 HTTP 服务器,最终再把内容返回给客户端。而在 Node.js 中,很多工作需要你自己来做(并不是都要自己动手,因为有第三方框架的帮助)。
1.2、 Express 框架
npm 提供了大量的第三方模块,其中不乏许多 Web 框架,我们没有必要重复发明轮子,
因而选择使用 Express 作为开发框架,因为它是目前最稳定、使用最广泛,而且 Node.js 官方推荐的唯一一个 We b 开发框架。 Express ( http://expressjs.com/ ) 除了为 http 模块提供了更高层的接口外,还实现了许多功能,其中包括:
路由控制;
模板解析支持;
动态视图;
用户会话;
CSRF 保护;
静态文件服务;
错误控制器;
访问日志;
缓存;
插件支持。
需要指出的是,Express 不是一个无所不包的全能框架,像 Rails 或 Django 那样实现了
模板引擎甚至 ORM (Object Relation Model,对象关系模型)。它只是一个轻量级的 Web 框架,多数功能只是对 HTTP 协议中常用操作的封装,更多的功能需要插件或者整合其他模块来完成。
1.3、 安装 Express
首先我们要安装 Express 。如果一个包是某个工程依赖,那么我们需要在工程的目录下使用本地模式安装这个包,如果要通过命令行调用这个包中的命令,则需要用全局模式安装,因此按理说我们使用本地模式安装 Express 即可。但是Express 像很多框架一样都提供了 Quick Start(快速开始)工具,这个工具的功能通常是建立一个网站最小的基础框架,在此基础上完成开发。当然你可以完全自己动手,但我还是推荐使用这个工具更快速地建立网站。为了使用这个工具,我们需要用全局模式安装 Express,因为只有这样我们才能在命令行中使用它。运行以下命令:
$ npm install -g express
等待数秒后安装完成,我们就可以在命令行下通过 express 命令快速创建一个项目了。
在这之前先使用 express --help 查看帮助信息:
Usage: express [options][path]
Options:
-s, --sessions add session support
-t, --template <engine> add template <engine> support(jade|ejs). default=jade -c, --css <engine> add stylesheet <engine> support(stylus). default=plain css
-v, --version output framework version
-h, --help output help information
Express 在初始化一个项目的时候需要指定模板引擎,默认支持Jade 和ejs,为了降低学
习难度我们推荐使用 ejs ,同时暂时不添加 CSS 引擎和会话支持。
1.4、建立工程
通过以下命令建立网站基本结构:express -e blog
当前目录下出现了子目录blog ,并且产生了一些文件:
它还提示我们要进入其中运行 npm install,我们依照指示,结果如下:它自动安装了依赖 ejs 和 express。这是为什么呢?检查目录中的 package.json 文件,内容是:
{ "name": "application-name", "version": "0.0.1", "private": true, "scripts": { "start": "node app.js" }, "dependencies": { "express": "3.4.7", "ejs": "*" } }
1.5、 启动服务器
用 Express 实现的网站实际上就是一个 Node.js 程序,因此可以直接运行。我们运行 node app.js,看到 Expressserver listening on port 3000 indevelopment mode。
接下来,打开浏览器,输入地址 http://localhost:3000,你就可以看到一个简单的 Welco me
to Express 页面了。如果你能看到如图所示的页面,那么说明你的设定正确无误。
1.6、工程的结构、现在让我们回过头来看看 Express 都生成了哪些文件。除了 package.json ,它只产生了两
个 JavaScript 文件 app.js 和 routes/index.js。模板引擎 ejs 也有两个文件 index.ejs 和layout.ejs ,此外还有样式表 style.css 。下面来详细看看这几个文件。
app.js:启动文件,或者说入口文件
package.json:存储着工程的信息及模块依赖,当在 dependencies中添加依赖的模块时,运行 npm install
,npm会检查当前目录下的 package.json,并自动安装所有指定的模块
node_modules:存放 package.json中安装的模块,当你在 package.json添加依赖的模块并安装后,存放在这个文件夹下
public:存放 image、css、js等文件
routes:存放路由文件
views:存放视图文件或者说模版文件
1. app.js
app.js 是工程的入口,我们先看看其中有什么内容:
/** * Module dependencies. */ var express = require('express'); var routes = require('./routes'); var user = require('./routes/user'); var http = require('http'); var path = require('path'); var app = express(); // all environments app.set('port', process.env.PORT || 3000); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.use(express.favicon()); app.use(express.logger('dev')); app.use(express.json()); app.use(express.urlencoded()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(path.join(__dirname, 'public'))); // development only if ('development' == app.get('env')) { app.use(express.errorHandler()); } app.get('/', routes.index); app.get('/users', user.list); http.createServer(app).listen(app.get('port'), function(){ console.log('Express server listening on port ' + app.get('port')); });
Express 依赖于 connect,提供了大量的中间件,可以通过 app.use 启用。app.configure
中启用了5个中间件:bodyParser 、methodOverride 、router、static 以及 errorHandler 。
bodyParser 的功能是解析客户端请求,通常是通过 POST 发送的内容。methodOverride
用于支持定制的 HTTP 方法。router 是项目的路由支持。static 提供了静态文件支持。
errorHandler 是错误控制器。
app.get('/', routes.index); 是一个路由控制器,用户如果访问“ / ”路径,则由routes.index 来控制。 最后服务器通过 app.listen(3000); 启动,监听3000端口。
http.createServer(app).listen(app.get('port'), function(){ console.log('Express server listening on port ' + app.get('port')); });
2. routes/index.js
routes/index.js 是路由文件,相当于控制器,用于组织展示的内容:
/* * GET home page. */ exports.index = function(req, res){ res.render('index', { title: 'Express' }); };
app.js 中通过 app.get('/', routes.index); 将“ / ”路径映射到 exports.index 函数下。其中只有一个语句 res.render('index', { title: 'Express' }),功能是调用模板解析引擎,翻译名为 index 的模板,并传入一个对象作为参数,这个对象只有一个属性,即 title:'Express'。
3. index.ejs
index.ejs 是模板文件,即 routes/index.js 中调用的模板,内容是:
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> </body>
</html>它的基础是 HTML 语言,其中包含了形如 <%=title %> 的标签,功能是显示引用的变量,即 res.render 函数第二个参数传入的对象的属性。
现在我们学习了如何创建一个工程并启动它,了解了工程的大体结构,下面我们将学习 express的基本使用及路由控制。
2、 路由控制
2.1 工作原理
当通过浏览器访问 app.js 建立的服务器时,会看到一个简单的页面,实际上它已经完成了许多透明的工作,现在就让我们来解释一下它的工作机制,以帮助理解网站的整体架构。
前面提到过 app.js中 app.get('/', routes.index)
可以用以下代码取代:
app.get('/', function(req, res){ res.render('index', { title: 'Express' }); };)
这段代码的意思是当访问主页时,调用ejs模板引擎,来渲染 index.ejs模版文件(即将 title变量全部替换为字符串 Express),生成静态页面并显示在浏览器中。我们来作一些修改,以上代码实现了路由的功能,我们当然可以不要 routes/index.js文件,把实现路由功能的代码都放在app.js里,但随着时间的推移 app.js会变得臃肿难以维护,这也违背了代码模块化的思想,所以我们把实现路由功能的代码都放在 routes/index.js里。官方给出的写法是在 app.js中实现了简单的路由分配,然后再去 index.js中找到对应的路由函数,最终实现路由功能。我们不妨把路由控制器和实现路由功能的函数都放到 index.js里,app.js 中只有一个总的路由接口。
打开 app.js,删除:
app.get('/', routes.index); app.get('/users', user.list);
注意:目前我们用不到 routes/user.js,也可以删除这个文件,同时删除 app.js中 var user = require('./routes/user');
。
在 app.js最后添加一行代码:
routes(app);
修改 index.js如下:
module.exports = function(app) { app.get('/', function (req, res) { res.render('index', { title: 'Express' }); }); };
现在,再运行你的 app,你会发现主页毫无二致。这里我们在 routes/index.js中通过 module.exports
导出了一个函数接口,在 app.js 中通过 require
加载了 index.js 然后通过 routes(app)
调用了 index.js 导出的函数。
由 Express 创建的网站架构如下:
这是一个典型的 MVC 架构,浏览器发起请求,由路由控制器接受,根据不同的路径定向到不同的控制器。控制器处理用户的具体请求,可能会访问数据库中的对象,即模型部分。控制器还要访问模板引擎,生成视图的 HTML,最后再由控制器返回给浏览器,完成一次请求。
express封装了多种 http 请求方式,我们主要只使用 get
和 post
两种,即 app.get()
和 app.post()
。app.get()
和 app.post()
的第一个参数都为请求的路径,第二个参数为处理请求的回调函数,回调函数有两个参数分别是 req 和res,代表请求信息和响应信息。
2.2 、创建路由规则
当我们在浏览器中访问譬如 http://localhost:3000/abc 这样不存在的页面时,服务器会在
响应头中返回 404 Not Found 错误,
这是因为 /abc 是一个不存在的路由规则,而且它也不是一个 public 目录下的文件,所以Express返回了404 Not Found的错误。
接下来我们会讲述如何创建路由规则。 假设我们要创建一个地址为 /hello 的页面,内容是当前的服务器时间,让我们看看具体做法。打开 app.js ,在已有的路由规则 app.get('/', routes.index) 后面添加一行:
app.get('/hello',routes.hello);
修改 routes/index.js,增加 hello函数:
/* * GET home page. */ exports.index = function(req, res) { res.render('index', { title: 'Express' }); exports.hello = function(req, res) { res.send('The time is ' + new Date().toString()); };
重启 app.js ,在浏览器中访问 http://localhost:3000/hello ,可以看到类似于下图的页面,
刷新页面可以看到时间发生变化,因为你看到的内容是动态生成的结果。
服务器在开始监听之前,设置好了所有的路由规则,当请求到达时直接分配到响应函数。app.get 是路由规则创建函数,它接受两个参数,第一个参数是请求的路径,第二个参数是一个回调函数,该路由规则被触发时调用回调函数,其参数表传递两个参数,分别是 req 和 res ,表示请求信息和响应信息。
很简单吧?我们学习了基本的路由规则及如何添加一条路由规则,马上我们将学习模板引擎的知识。
3.1、 模板引擎
在这里,我们会讲述模板引擎的使用和集成,也就是视图。视图决定了用户最终能看到什么,因此也是最重要部分,这里我们以 ejs 为例介绍模板引擎的使用方法。
模板引擎的功能是将页面模板和要显示的数据结合起来生成 HTML 页面。它既可以运
行在服务器端又可以运行在客户端,大多数时候它都在服务器端直接被解析为 HTML,解析完成后再传输给客户端,因此客户端甚至无法判断页面是否是模板引擎生成的。有时候模板引擎也可以运行在客户端,即浏览器中,典型的代表就是 XSLT ,它以 XML 为输入,在客户端生成 HTML 页面。但是由于浏览器兼容性问题,XSLT 并不是很流行。目前的主流还是由服务器运行模板引擎。
3.2、 使用模板引擎
基于 JavaScript 的模板引擎有许多种实现,我们推荐使用 ejs (Embedded JavaScript ),
因为它十分简单,而且与 Express 集成良好。由于它是标准 JavaScript 实现的,因此它不仅可以运行在服务器端,还可以运行在浏览器中。我们这一章的示例是在服务器端运行 ejs ,
这样减少了对浏览器的依赖,而且更符合传统架构的习惯。
我们在 app.js 中通过以下两个语句设置了模板引擎和页面模板的位置:
app.set('views', __dirname +'/views');
app.set('view engine', 'ejs');
表明要使用的模板引擎是 ejs ,页面模板在 views 子目录下。在 routes/index.js 的
exports.index 函数中通过如下语句调用模板引擎:
res.render('index', { title:'Express' });
res.render 的功能是调用模板引擎,并将其产生的页面直接返回给客户端。它接受
两个参数,第一个是模板的名称,即 views 目录下的模板文件名,不包含文件的扩展名;第二个参数是传递给模板的数据,用于模板翻译。index.ejs 内容如下:
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> </body>
</html>上面代码其中有两处 <%= title %> ,用于模板变量显示,它们在模板翻译时会被替换成 Express ,因为 res.render 传递了 { title: 'Express' }。
ejs 的标签系统非常简单,它只有以下3种标签。
<% code %>:JavaScript 代码。
<%= code %> :显示替换过 HTML 特殊字符的内容。
<%- code %> :显示原始 HTML 内容。
我们可以用它们实现页面模板系统能实现的任何内容。
渲染后生成的页面代码为:
<!DOCTYPE html> <html> <head> <title>Express</title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1>Express</h1> <p>Welcome to Express</p> </body> </html>