闵超的主页

使用Node.js进行Web开发

2017-08-16
闵超

使用Node.js进行Web开发

学习了前四课的东西,已经学习了许多知识,但还缺乏实战性的内容。现在,我们开始从打算从零开始使用Node.js实现一个博客系统,功能包括路由控制、页面模板、数据库访问、用户注册、登陆、用户会话等内容。

我会先介绍Express框架,MVC设计模式、ejs模板引擎以及MongoDB数据库的操作,通过实战演练,我们会了解到网站开发的基本方式。

准备工作

在开始动手之前,我们首先要大致知道 Node.js 实现网站的工作原理。Node.js 和 PHP、Perl、ASP、JSP 一样,目的都是实现动态网页,也就是说由服务器动态生成 HTML 页面。之所以要这么做,是因为静态 HTML 的可扩展性非常有限,无法与用户有效交互。

同时如果有大量相似的内容,例如产品介绍页面,那么1000个产品就要1000个静态的 HTML 页面,维护这1000个页面简直是一场灾难,因此动态生成 HTML 页面是必要的。

这里插播一些题外话:

最早实现动态网页的方法是使用Perl (C++等任何语言都可以,Perl只是最常见的)和 CGI。

在 Perl 程序中输出 HTML 内容,由 HTTP服务器调用 Perl 程序,将结果返回给客户端。这种方式在互联网刚刚兴起的 20 世纪 90 年代非常流行,几乎所有的动态网页都是这么做的。

但问题在于如果 HTML 内容比较多,维护非常不方便。

大概在 2000 年左右,以 ASP(<% %>)、PHP(?php?)、JSP 的为代表的以模板为基础的语言出现了,这种语言的使用方法与 CGI 相反,是在以 HTML 为主的模板中插入程序代码。这种方式在2002年前后非常流行,但它的问题是页面和程序逻辑紧密耦合,任何一个网站规模变大以后,都会遇到结构混乱,难以处理的问题。

为了解决这种问题,以 MVC 架构为基础的平台逐渐兴起,著名的 Ruby on Rails、Django、Zend Framework 都是基于 MVC 架构的。

MVC (Model-View-Controller,模型视图控制器)是一种软件的设计模式,它最早是由 20 世纪 70 年代的 Smalltalk 语言提出的,即把一个复杂的软件工程分解为三个层面:模型、视图和控制器。

  • 模型是对象及其数据结构的实现,通常包含数据库操作。
  • 视图表示用户界面,在网站中通常就是 HTML 的组织结构。
  • 控制器用于处理用户请求和数据流、复杂模型,将输出传递给视图。

PHP、ASP、JSP 为“模板为中心的架构”与MVC架构的比较

模板为中心的架构与MVC架构比较.png

这两种架构都出自原始的 CGI,但不同之处是前者走了一条粗放扩张的发展路线,由于易学易用,在几年前应用较广,而随着互联网规模的扩大,后者优势逐渐体现,目前已经成为主流。

Node.js 本质上和 Perl 或 C++ 一样,都可以作为 CGI 扩展被调用,但它还可以跳过 HTTP服务器,因为它本身就是。传统的架构中 HTTP 服务器的角色会由 Apache、Nginx、IIS 之类的软件来担任,而 Node.js 不需要。Node.js 提供了 http 模块,它是由 C++ 实现的,性能可靠,可以直接应用到生产环境。

node.js服务器与传统服务器的比较

node服务器与传统服务器的比较.png

Express框架

npm 提供了大量的第三方模块,其中不乏许多 Web 框架,我们没有必要重复造轮子,因而选择使用 Express 作为开发框架,因为它是目前最稳定、使用最广泛,而且 Node.js 官方推荐的唯一一个 Web 开发框架。

Express ( http://expressjs.com/ ) 除了为 http 模块提供了更高层的接口外,还实现了许多功能,其中包括:

  1. 路由控制;
  2. 模板解析支持;
  3. 动态视图;
  4. 用户会话;
  5. CSRF 保护;
  6. 静态文件服务;
  7. 错误控制器;
  8. 访问日志;
  9. 缓存;
  10. 插件支持。

但是,Express 不是一个无所不包的全能框架,它只是一个轻量级的 Web 框架,多数功能只是对 HTTP 协议中常用操作的封装,更多的功能需要插件或者整合其他模块来完成。

var express = require('express');
var app = express.createServer();
app.use(express.bodyParser());
app.all('/', function(req, res) {
	res.send(req.body.title + req.body.text);
});
app.listen(3000);

可以看到,我们不需要手动编写 req 的事件监听器了,只需加载 express.bodyParser()就能直接通过 req.body 获取 POST 的数据了。

安装Express

首先我们要安装 Express。如果一个包是某个工程依赖,那么我们需要在工程的目录下使用本地模式安装这个包,如果要通过命令行调用这个包中的命令,则需要用全局模式安装,因此按理说我们使用本地模式安装 Express 即可。

但是Express 像很多框架一样都提供了 Quick Start(快速开始)工具,这个工具的功能通常 是建立一个网站最小的基础框架,在此基础上完成开发。

$ npm install -g express

等待数秒后安装完成,我们就可以在命令行下通过 express 命令快速创建一个项目了。在这之前先使用 express –help 查看帮助信息或者express –version查看版本

建立工程

express -e ejs microblog

当前目录下出现了子目录 microblog,并且产生了一些文件:

create : microblog
create : microblog/package.json
create : microblog/app.js
create : microblog/public
create : microblog/public/javascripts
create : microblog/public/images
create : microblog/public/stylesheets
create : microblog/public/stylesheets/style.css
create : microblog/routes
create : microblog/routes/index.js
create : microblog/views
create : microblog/views/layout.ejs
create : microblog/views/index.ejs
dont forget to install dependencies:

install dependencies:
     > cd microblog && npm install

run the app:
     > SET DEBUG=microblog:* & npm start

它还提示我们要进入其中运行 npm install,和如何启动microblog这个项目。我们依照指示

cd  microblog
npm install

检查package.json,并自动安装所有依赖

{
  "name": "microblog",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.17.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.3",
    "express": "~4.15.2",
    "jade": "~1.11.0",
    "morgan": "~1.8.1",
    "serve-favicon": "~2.4.2"
  }
}

启动服务器

前三节的http服务器都是由node app.js来启动的,既然express是框架,不可能一次次执行服务器把,框架的好处,就是不影响写代码的情况下简化各种操作。因此,这里就有了简单的一条命令:

npm start **记住,这条命令在你的node生涯中至关重要**

要关闭服务器的话,在终端中按 Ctrl + C。注意,如果你对代码做了修改,要想看到修改后的效果必须重启服务器,也就是说你需要关闭服务器并再次运行才会有效果。如果觉得有些麻烦,可以使用 supervisor 实现监视代码修改和自动重启。

工程的结构

现在让我们回过头来看看 Express 都生成了哪些文件。

除了 package.json,它只产生了两个 JavaScript 文件 app.js 和 routes/index.js。

模板引擎 ejs 也有两个文件 index.ejs 和layout.ejs,此外还有样式表 style.css。

  1. app.js

    app.js 是工程的入口,我们先看看其中有什么内容:

    // app.js
    	
    //模板导入
    var express = require('express');
    var path = require('path');
    var favicon = require('serve-favicon');
    var logger = require('morgan');
    var cookieParser = require('cookie-parser');
    var bodyParser = require('body-parser');
    //以上是公共模块导入,不同的express版本自动加载的模块不同,但是区别不大
    	
    //创建路由
    var index = require('./routes/index');
    var users = require('./routes/users');
    	
    var app = express();
    	
    // 视图引擎设置
    app.set('views', path.join(__dirname, 'views'));//设置视图路径
    app.set('view engine', 'ejs');//设置视图模板也有是ejs版本
    //通过npm安装Express4.x版本之后发现默认的视图模板是jade的,如果想使用ejs模板的话先通过npm安装ejs
    //npm -e ejs microblog创建是ejs模板引擎
    //npm -t ejs microblog创建是jade模板引擎
    	
    // 取消注释用/public下的ico 代替
    //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
    	
    app.use(logger('dev'));//应用程序使用(记录器('dev”)
    app.use(bodyParser.json());	//bodyParse功能是解析客户端请求
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(cookieParser());
    app.use(express.static(path.join(__dirname, 'public')));//静态文件存放目录
    	
    //路由控制器
    app.use('/', index);//用户如果访问“ / ”路径,则由 routes.index 来控制。
    app.use('/users', users);//用户如果访问“ /users ”路径,则由 routes.users 来控制。
    	
    // 404页面
    app.use(function(req, res, next) {
      var err = new Error('Not Found');
      err.status = 404;
      next(err);
    });
    	
    // 404报错处理
    app.use(function(err, req, res, next) {
      // set locals, only providing error in development
      res.locals.message = err.message;
      res.locals.error = req.app.get('env') === 'development' ? err : {};
    	
      // 渲染错误页面
      res.status(err.status || 500);
      res.render('error');
    });
    	
    //输出app模块
    module.exports = app;
    

    代码解释:

    app.set 是 Express 的参数设置工具,接受一个键(key)和一个值(value),可用的参数有。

    • basepath:基础地址,通常用于 res.redirect() 跳转。
    • views:视图文件的目录,存放模板文件。
    • view engine:视图模板引擎。
    • view options:全局视图参数对象。
    • view cache:启用视图缓存。
    • case sensitive routes:路径区分大小写。
    • strict routing:严格路径,启用后不会忽略路径末尾的“ / ”。
    • jsonp callback:开启透明的 JSONP 支持。

    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端口。

  2. routes/index.js

    routes/index.js 是路由文件,相当于控制器,用于组织展示的内容:

    var express = require('express');
    var router = express.Router();
    	
    /* GET home page. */
    router.get('/', function(req, res, next) {
      res.render('index', { title: 'Express' });//调用index模板引擎,title作为参数传入
    });
    	
    module.exports = router;
    

    代码解释:app.js中通过app.get(‘/’, routes.index); 将“ / ”路径映射到 exports.index函数下。其中只有一个语句 res.render(‘index’, { title: ‘Express’ }),功能是调用模板解析引擎,翻译名为 index 的模板,并传入一个对象作为参数,这个对象只有一个属性,即 title: ‘Express’。

  3. 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>
    

模板文件不是孤立展示的,默认情况下所有的模板都继承自 layout.ejs,即 <%- body %>部分才是独特的内容,其他部分是共有的,可以看作是页面框架。

新的版本中是没有layout.ejs的,具体看版本

路由控制

工作原理

当通过浏览器访问 app.js 建立的服务器时,会看到一个简单的页面,实际上它已经完成了许多透明的工作,现在就让我们来解释一下它的工作机制,以帮助理解网站的整体架构。

访问http://localhost:3000,浏览器会向服务器发送以下请求:

GET / HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.142
Safari/535.19
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: UTF-8,*;q=0.5

其中第一行是请求的方法、路径和 HTTP 协议版本,后面若干行是 HTTP 请求头。app 会解析请求的路径,调用相应的逻辑。

app.js 中有一行内容是 app.get(‘/’, routes.index),它的作用是规定路径为“/”的 GET 请求由 routes.index 函数处理。

routes.index 通过 res.render(‘index’, { title: ‘Express’ }) 调用视图模板 index,传递 title变量。

Express创建的网站架构.png

这是一个典型的 MVC 架构,浏览器发起请求,由路由控制器接受,根据不同的路径定向到不同的控制器。控制器处理用户的具体请求,可能会访问数据库中的对象,即模型部分。控制器还要访问模板引擎,生成视图的HTML,最后再由控制器返回给浏览器,完成一次请求。

创建路由规则

当我们在浏览器中访问譬如 http://localhost:3000/abc 这样不存在的页面时,服务器会在响应头中返回 404 Not Found 错误。

这是因为 /abc 是一个不存在的路由规则,而且它也不是一个 public 目录下的文件,所以Express返回了404 Not Found的错误。

下面是一个简单例子:添加一个显示时间的路由规则。

  • 在app.js中,添加创建路由hello,用于导入模板

    ...
    //创建路由
    var index = require('./routes/index');
    var users = require('./routes/users');
    	
    var hello = require('./routes/hello');
    ...
    //路由控制器
    app.use('/', index);//用户如果访问“ / ”路径,则由 routes.index 来控制。
    app.use('/users', users);//用户如果访问“ /users ”路径,则由 routes.users 来控制。
    	
    app.use('/hello', hello);//用户如果访问/hello路径,则由routes.hello来控制
    ...
    
  • 在routers创建hello.js文件用来控制路由

    //hello.js
    var express = require('express');
    var router = express.Router();
    	
    /* GET home page. */
    router.get('/', function(req, res, next) {
      res.send('现在时间是: ' + new Date().toString());
    });
    	
    	
    module.exports = router;
    

保存,重启服务器npm start

在页面中输入localhost:3000/hello

你会看见页面中输出

现在时间是: Thu Aug 07 2017 13:16:32 GMT+0800 (中国标准时间)

路由匹配

上面的例子是为固定的路径设置路由规则,Express 还支持更高级的路径匹配模式。例如我们想要展示一个用户的个人页面,路径为 /user/[username]:

看一个例子: 在routes文件夹下的users.js中添加如下代码:

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

router.get('/:username', function(req, res) {
	res.send('用户名: ' + req.params.username);
});

module.exports = router; 注意是/:username这样添加,你可以理解为子路由。

重启服务器,npm start 浏览器输入localhost:3000/users/minchao

结果是:用户名: minchao

浏览器输入localhost:3000/users/micale 结果是:用户名: micale

REST风格的路由规则

Express 支持 REST 风格的请求方式,在介绍之前我们先说明一下什么是 REST。

REST的意思是 表征状态转移(Representational State Transfer),它是一种基于 HTTP 协议的网络应用的接口风格,充分利用 HTTP 的方法实现统一风格接口的服务。HTTP 协议定义了以下8种标准的方法。

  1. GET:请求获取指定资源。
  2. HEAD:请求指定资源的响应头。
  3. POST:向指定资源提交数据。
  4. PUT:请求服务器存储一个资源。
  5. DELETE:请求服务器删除指定资源。
  6. TRACE:回显服务器收到的请求,主要用于测试或诊断。
  7. CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
  8. OPTIONS:返回服务器支持的HTTP请求方法。

其中我们经常用到的是 GET、POST、PUT 和 DELETE 方法。根据 REST 设计模式,这4种方法通常分别用于实现以下功能。

  • GET:获取(查)
  • POST:新增(增)
  • PUT:更新(改)
  • DELETE:删除(删)

Express 支持的 HTTP 请求的绑定函数:

请求方式		绑定函数
GET 		app.get(path, callback)
POST 		app.post(path, callback)
PUT 		app.put(path, callback)
DELETE 		app.delete(path, callback)
PATCH		app.patch(path, callback)
TRACE 		app.trace(path, callback)
CONNECT 	app.connect(path, callback)
OPTIONS 	app.options(path, callback)
所有方法 	app.all(path, callback)

需要注意的是 app.all 函数,它支持把所有的请求方式绑定到同一个响应函数,是一个非常灵活的函数,在后面我们可以看到许多功能都可以通过它来实现。

路由控制权转移

当你访问任何被这两条同样的规则匹配到的路径时,会发现请求总是被前一条路由规则捕获,后面的规则会被忽略。原因是Express在处理路由规则时,会优先匹配先定义的路由规则:例如修改users.js文件:

//users.js
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

router.all('/:username', function(req, res) {
	res.send('我会优先捕获并展示,下面相同的就跳过!');
});

router.get('/:username', function(req, res) {
	res.send('用户名: ' + req.params.username);
});

module.exports = router; 如上代码中,我们设置了两个路由router.all和route.get路径是相同的。

在浏览器中输入localhost:3000/users/minchao

页面会展示

我会优先捕获并展示,下面相同的就跳过!

既然下面的没有用,那怎么才能让下面相同的路由来决定跳转和输出对象,而当前只处理一些其他事情呢。Express 提供了路由控制权转移的方法,即回调函数的第三个参数next,通过调用next(),会将路由控制权转移给后面的规则。例如继续在route/users.js:

	//users.js
	var express = require('express');
	var router = express.Router();
	
	/* GET users listing. */
	router.get('/', function(req, res, next) {
	  res.send('respond with a resource');
	});
	
	router.all('/:username', function(req, res,next) {
		 console.log("别看我,我只是假动作");
		 next();
	});
	
	router.get('/:username', function(req, res) {
		res.send('用户名: ' + req.params.username);
	});
	
	module.exports = router;

重启服务器:npm start

在浏览器中输入:localhost:3000/users/minchao

你会在浏览器中发现:

路由控制权转移浏览器.png

你会在服务器后台发现:

路由控制权转移.png

代码解释:能在后台打印出”别看我,我只是假动作”,说明请求会先被第一条路由规则捕获,再有next()分发给下一条路由规则捕获,页面就会出现 用户名:minchao

用法:这是一个非常有用的工具,可以让我们轻易地实现中间件,而且还能提高代码的复用程度。

例如我们针对一个用户查询信息和修改信息的操作,分别对应了 GET 和 PUT 操作,而两者共有的一个步骤是检查用户名是否合法,因此可以通过 next() 方法实现:

var users = {
	'minchao': {
		name: 'minchao',
		website: 'http://minchao.me'
	}
};
app.all('/user/:username', function(req, res, next) {
	// 检查用户是否存在
	if (users[req.params.username]) {
		next();
	} else {
		next(new Error(req.params.username + '不存在'));
	}
});
app.get('/user/:username', function(req, res) {
	// 用户一定存在,直接展示
	res.send(JSON.stringify(users[req.params.username]));
});
app.put('/user/:username', function(req, res) {
	// 修改用户信息
	res.send('Done');
});

上面例子中,app.all 定义的这个路由规则实际上起到了中间件的作用,把相似请求的相同部分提取出来,有利于代码维护其他next方法如果接受了参数,即代表发生了错误。使用这种方法可以把错误检查分段化,降低代码耦合度。

模板引擎

Express 的路由控制方法,它是网站架构最核心的部分,即MVC架构中的控制器(C)。而中间的视图(V)就是本章要讲的模板引擎。视图决定了用户最终会看到什么,因此也是最重要的部分,这里我们以ejs为例介绍模板引擎的使用方法。

什么是模板引擎

定义:模板引擎(Template Engine)是一个从页面模板根据一定的规则生成HTML的工具。

PHP 原本是 Personal Home Page Tools(个人主页工具)的简称,用于取代 Perl 和 CGI 的组合,其功能是让代码嵌入在 HTML 中执行,以产生动态的页面,因此 PHP 堪称是最早的模板引擎的雏形。

随后的 ASP、JSP 都沿用了这个模式,即建立一个 HTML 页面模板,插入可执行的代码,运行时动态生成HTML。

按照这种模式,整个网站就由一个个的页面模板组成,所有的逻辑都嵌入在模板中。这种模式大大降低了动态网页开发的门槛,因此一开始很受欢迎,但随着规模的扩大它会遇到许多问题,下面列举几个主要的。

  • 页面功能逻辑与页面布局样式耦合,网站规模变大以后逐渐难以维护。
  • 语法复杂,对于非技术的网页设计者来说门槛较高,难以学习。
  • 功能过于全面,页面设计者可以在页面上编程,不利于功能划分,也使模板解析效率降低。

这些问题制约了早期模板引擎的发展,直到 MVC 开发模式普及,模板引擎才开始遍地开花。

功能:模板引擎的功能是将页面模板和要显示的数据结合起来生成 HTML 页面。它既可以运行在服务器端又可以运行在客户端,大多数时候它都在服务器端直接被解析为 HTML,解析完成后再传输给客户端。

在 MVC 架构中,模板引擎包含在服务器端。控制器得到用户请求后,从模型获取数据调用模板引擎。模板引擎以数据和页面模板为输入,生成 HTML 页面,然后返回给控制器,由控制器交回客户端。

模板引擎在MVC中的位置.png

意思是:MVC框架中,由C(获取用户请求)->M(从模型中获取数据)->C(获取数据调用模板引擎生成页面)->V(传给客户端浏览器展现视图)

使用模板引擎

基于 JavaScript 的模板引擎有许多种实现,我们推荐使用 ejs (Embedded JavaScript),因为它十分简单,而且与 Express 集成良好。

目前主流的模板引擎是ejs,最新的express中目前默认的引擎模板是jade,我的GitHub上有一个简单的小项目,因为当时不了解node的框架使用的html引擎,node+mysql+express小项目在数据库mysql方面暴露的很多问题。

我们在 app.js 中通过以下两个语句设置了模板引擎和页面模板的位置:

//app.js
...
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
...

//app.js
//...
//app.set('views', __dirname + '/views');
//app.set('view engine', 'jade');
//...

//app.js
//...
//app.set('views', __dirname + '/views');
//app.set('view engine', 'html');

//... 代码部分表明使用的模板引擎是ejs,页面模板在views子目录下,routes/index.js的exports.index函数中通过res.render()来调用模板引擎:

res.render('index', { title: 'Express' });

res.render的功能是调用模板引擎,并将其产生的页面直接返回给浏览器。

它接受两个参数,第一个是模板的名称,即 views 目录下的模板文件名,不包含文件的扩展名;第 二个参数是传递给模板的数据,用于模板翻译。

views下的index.ejs内容是

...
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
...

用法:

ejs 的标签系统非常简单,它只有以下3种标签。

  • <% code %>:JavaScript 代码。
  • <%= code %>:显示替换过 HTML 特殊字符的内容。
  • <%- code %>:显示原始 HTML 内容。

我们可以用它们实现页面模板系统能实现的任何内容。

layout.ejs

layout.ejs,其目的是用于页面布局模板,默认情况下每个单独的页面都继承自这个框架,替换掉其中的<%- body %>部分,这个功能非常有用,因为一般为了保持整个网站的一直风格,HTML页面的<head>部分以及页面页脚中的大量内容是重复的,因此,我们可以把它们放在layout.ejs中。

当然,这个功能并不是强制的,如果想关闭它,可以在app.js的app.configure中添加以下内容来打开和关闭它。

//关闭layout
app.set('view options', {
	layout: false
});
//打开
app.set('view options', {
	layout: true
});

另一种情况是,一个网站可能需要不止一种页面布局,例如网站分前台展示和后台管理系统,两者的页面结构有很大的区别,一套页面布局不能满足需求。这时我们可以在页面模板翻译时指定页面布局,即设置 layout 属性。

function(req, res) {
	res.render('userlist', {
		title: '用户列表后台管理系统',
		layout: 'admin'
	})
);

用于在render中指定模板。

片段视图

Express 的视图系统还支持片段视图 (partials),它就是一个页面的片段,通常是重复的内容,用于迭代显示。一般使用方法是将相对独立的页面分割出去,而且可以避免显式地使用for循环。

app.get('/list', function(req, res) {
	res.render('list', {
		title: 'List',
		items: [1991, 'byvoid', 'express', 'Node.js']
	});
});
//传输数据

在 views 目录下新建 list.ejs,内容是:

<ul><%- partial('listitem', items) %></ul>

同时新建 listitem.ejs,内容是:

<li><%= listitem %></li>

partial 是一个可以在视图中使用函数,它接受两个参数,第一个是片段视图的名称,第二个可以是一个对象或一个数组,如果是一个对象,那么片段视图中上下文变量引用的就是这个对象;如果是一个数组,那么其中每个元素依次被迭代应用到片段视图。片段视图中上下文变量名就是视图文件名,例如上面的’listitem’。

视图助手

Express 提供了一种叫做视图助手的工具,它的功能是允许在视图中访问一个全局的函数或对象,不用每次调用视图解析的时候单独传入。前面提到的 partial 就是一个视图助手。

视图助手有两类,分别是静态视图助手和动态视图助手。这两者的差别在于:

静态视图助手可以是任何类型的对象,包括接受任意参数的函数,但访问到的对象必须是与用户请求无关的。

动态视图助手只能是一个函数,这个函数不能接受参数,但可以访问 req 和 res 对象。

看了第四章,发现了一些问题,由于版本的不同,我的express的ejs模板默认是没有layout.ejs的。

导致片段视图中的demo没有获得想要的效果。希望知道的人能告诉我模板引擎修改的方法。


Content

如果喜欢,打个赏,加个好友吧

扫描二维码打赏

扫描二维码打赏