Node.js开发指南学习笔记

1.1     快速入门

1.1.1    异步式I/O与事件式编程

异步式 I/O (Asynchronous I/O)或非阻塞式 I/O (Non-blocking I/O)则针对所有 I/O 操作不采用阻塞的策略。当线程遇到 I/O 操作时,不会以阻塞的方式等待 I/O 操作的完成或数据的返回,而只是将 I/O 请求发送给操作系统,继续执行下一条语句。当操作系统完成 I/O 操作时,以事件的形式通知执行 I/O 操作的线程,线程会在特定时候处理这个事件。为了处理异步 I/O,线程必须有事件循环,不断地检查有没有未处理的事件,依次予以处理。

1.1.2    Node.js 的事件循环机制

Node.js 在什么时候会进入事件循环呢?答案是 Node.js 程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数,所以 Node.js 始终在事件循环中,程序入口就是事件循环第一个事件的回调函数。事件的回调函数在执行的过程中,可能会发出 I/O 请求或直接发射(emit)事件,执行完毕后再返回事件循环,事件循环会检查事件队列中有没有未处理的事件,直到程序结束。

与其他语言不同的是,Node.js没有显式的事件循环,类似Ruby 的EventMachine::run()的函数在 Node.js 中是不存在的。Node.js 的事件循环对开发者不可见,由 libev 库实现。libev支持多种类型的事件,如ev_io、ev_timer、ev_signal、ev_idle 等,在 Node.js 中均被EventEmitter 封装。libev 事件循环的每一次迭代,在 Node.js 中就是一次 Tick,libev 不断检查是否有活动的、可供检测的事件监听器,直到检测不到时才退出事件循环,进程结束。

1.1.3    模块和包

Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。

模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个Node.js文件就是一个模块,这个文件可能是 JavaScript 代码、JSON 或者编译过的 C/C++ 扩展。

包是在模块基础上更深一步的抽象,Node.js 的包类似于 C/C++ 的函数库或者 Java/.Net的类库。它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制。

我们经常把 Node.js 的模块和包相提并论,因为模块和包是没有本质区别的,两个概念也时常混用。如果要辨析,那么可以把包理解成是实现了某个功能模块的集合,用于发布和维护。对使用者来说,模块和包的区别是透明的,因此经常不作区分。

1.1.4    调试

当代码被改动时,运行的脚本会被终止,然后重新启动node app.js。supervisor 命令启动 app.js,只需刷新页面,方便调试。

1.2     核心模块

1.2.1    全局对象

Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global对象的属性。在 Node.js 中能够直接访问到对象通常都是 global 的属性,如 console、process等。

global 最根本的作用是作为全局变量的宿主。

1.2.1.1        process

process 是一个全局变量,即 global 对象的属性。它用于描述当前 Node.js 进程状态的对象,提供了一个与操作系统的简单接口。

process.argv是命令行参数数组

process.stdout是标准输出流,通常我们使用的 console.log() 向标准输出打印字符,而 process.stdout.write() 函数提供了更底层的接口。

process.stdin是标准输入流,初始时它是被暂停的,要想从标准输入读取数据,你必须恢复流,并手动编写流的事件响应函数。

process.nextTick(callback)的功能是为事件循环设置一项任务,Node.js 会在下次事件循环调响应时调用 callback。

1.2.1.2        console

console.log():向标准输出流打印字符并以换行符结束。console.log 接受若干个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则以类似于 C 语言 printf() 命令的格式输出。第一个参数是一个字符串,如果没有参数,只打印一个换行。

console.error():与 console.log() 用法相同,只是向标准错误流输出。

console.trace():向标准错误流输出当前的调用栈。

1.2.1.3        util

util.inherits(constructor,superConstructor)是一个实现对象间原型继承的函数。JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同。

util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象。

showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。

depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多少。如果不指定depth,默认会递归2层,指定为 null 表示将不限递归层数完整遍历对象。

如果color 值为 true,输出格式将会以 ANSI 颜色编码,通常用于在终端显示更漂亮的效果。

特别要指出的是,util.inspect并不会简单地直接把对象转换为字符串,即使该对象定义了 toString 方法也不会调用。

1.2.2    events

events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件发射与事件监听器功能的封装。EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持若干个事件监听器。当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。

1.2.2.1        EventEmitter常用的API

EventEmitter.on(event, listener) 为指定事件注册一个监听器,接受一个字

符串 event 和一个回调函数 listener。

EventEmitter.emit(event, [arg1], [arg2],[...]) 发射 event 事件,传递若干可选参数到事件监听器的参数表。

EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器。

EventEmitter.removeListener(event,listener) 移除指定事件的某个监听器,listener必须是该事件已经注册过的监听器。

EventEmitter.removeAllListeners([event]) 移除所有事件的所有监听器,如果指定 event,则移除指定事件的所有监听器。

1.2.2.2        error 事件

EventEmitter 定义了一个特殊的事件 error,它包含了“错误”的语义,我们在遇到

异常的时候通常会发射error 事件。当 error 被发射时,EventEmitter 规定如果没有响

应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。我们一般要为会发射 error

事件的对象设置监听器,避免遇到错误后整个程序崩溃。

1.2.3    fs

fs 模块是文件操作的封装,它提供了文件的读取、写入、更名、删除、遍历目录、链接等 POSIX 文件系统操作。与其他模块不同的是,fs 模块中所有的操作都提供了异步的和同步的两个版本。

1.2.3.1        fs.readFile

fs.readFile(filename,[encoding],[callback(err,data)])是最简单的读取文件的函数。它接受一个必选参数 filename,表示要读取的文件名。第二个参数 encoding是可选的,表示文件的字符编码。callback 是回调函数,用于接收文件的内容。如果不指定 encoding,则 callback 就是第二个参数。回调函数提供两个参数 err 和 data,err 表示有没有错误发生,data是文件内容。如果指定了encoding,data 是一个解析后的字符串,否则 data 将会是以 Buffer 形式表示的二进制数据。

 

Node.js 的异步编程接口习惯是以函数的最后一个参数为回调函数,通常一个函数只有一个回调函数。回调函数是实际参数中第一个是 err,其余的参数是其他返回的内容。如果没有发生错误,err 的值会是 null 或undefined。如果有错误发生,err 通常是 Error 对象的实例。

1.2.3.2        fs.readFileSync

fs.readFileSync(filename, [encoding])是 fs.readFile 同步的版本。它接受的参数和 fs.readFile 相同,而读取到的文件内容会以函数返回值的形式返回。如果有错误发生,fs 将会抛出异常,你需要使用 try 和 catch 捕捉并处理异常。

与同步 I/O 函数不同,Node.js 中异步函数大多没有返回值。

1.2.3.3        fs.open

fs.open(path, flags, [mode], [callback(err,fd)])是 POSIX open 函数的封装,与 C 语言标准库中的 fopen 函数类似。它接受两个必选参数,path 为文件的路径,lags 可以是以下值。

 r :以读取模式打开文件。

 r+ :以读写模式打开文件。

 w :以写入模式打开文件,如果文件不存在则创建。

 w+ :以读写模式打开文件,如果文件不存在则创建。

 a :以追加模式打开文件,如果文件不存在则创建。

 a+ :以读取追加模式打开文件,如果文件不存在则创建。

mode 参数用于创建文件时给文件指定权限,默认是 0666①。回调函数将会传递一个文件描述符 fd。

1.2.3.4        fs.read

fs.read(fd, buffer, offset, length,position, [callback(err, bytesRead,buffer)])是 POSIX read 函数的封装,相比 fs.readFile 提供了更底层的接口。fs.read的功能是从指定的文件描述符 fd 中读取数据并写入buffer 指向的缓冲区对象。offset是buffer 的写入偏移量。length 是要从文件中读取的字节数。position 是文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。回调函数传递bytesRead 和 buffer,分别表示读取的字节数和缓冲区对象。

 

1.2.3.5        fs所有函数的定义和功能

fs 模块函数表

功 能

异步函数

同步函数

打开文件

fs.open(path,flags,[mode], [callback(err,fd)])

fs.openSync(path, flags, [mode])

关闭文件

fs.close(fd, [callback(err)])

fs.closeSync(fd)

读取文件(文件描述符)

fs.read(fd,buffer,offset,length,position,

[callback(err, bytesRead, buffer)])

fs.readSync(fd,buffer,offset,length,position)

 

写入文件(文件描述符)

fs.write(fd,buffer,offset,length,position,

[callback(err, bytesWritten, buffer)])

fs.writeSync(fd,buffer,offset,length,position)

读取文件内容

fs.readFile(filename,[encoding],[callback(err, data)])

fs.readFileSync(filename,[encoding])

删除文件

fs.unlink(path, [callback(err)])

fs.unlinkSync(path)

删除目录

fs.rmdir(path, [callback(err)])

fs.rmdirSync(path)

读取目录

fs.readdir(path, [callback(err, files)])

fs.readdirSync(path)

获取真实路径

fs.realpath(path, [callback(err,resolvedPath)])

fs.realpathSync(path)

更名

fs.rename(path1, path2, [callback(err)])

fs.renameSync(path1, path2)

截断

fs.truncate(fd, len, [callback(err)])

fs.truncateSync(fd, len)

更改所有权

fs.chown(path, uid, gid, [callback(err)])

fs.chownSync(path, uid, gid)

更改所有权(文件描述符)

fs.fchown(fd, uid, gid, [callback(err)])

fs.fchownSync(fd, uid, gid)

更改所有权(不解析符号链接)

fs.lchown(path, uid, gid, [callback(err)])

fs.lchownSync(path, uid, gid)

更改权限

fs.chmod(path, mode, [callback(err)])

fs.chmodSync(path, mode)

更改权限(文件描述符)

fs.fchmod(fd, mode, [callback(err)])

fs.fchmodSync(fd, mode)

更改权限(不解析符号链接)

fs.lchmod(path, mode, [callback(err)])

fs.lchmodSync(path, mode)

获取文件信息

fs.stat(path, [callback(err, stats)])

fs.statSync(path)

获取文件信息(文件描述符)

fs.fstat(fd, [callback(err, stats)])

fs.fstatSync(fd)

获取文件信息(不解析符号链接)

fs.lstat(path, [callback(err, stats)])

fs.lstatSync(path)

创建硬链接

fs.link(srcpath, dstpath, [callback(err)])

fs.linkSync(srcpath, dstpath)

创建符号链接

fs.symlink(linkdata, path, [type],[callback(err)])

fs.symlinkSync(linkdata, path,[type])

读取链接

fs.readlink(path, [callback(err,linkString)])

fs.readlinkSync(path)

修改文件时间戳

fs.utimes(path, atime, mtime, [callback(err)])

fs.utimesSync(path, atime, mtime)

修改文件时间戳(文件描述符)

fs.futimes(fd, atime, mtime, [callback(err)])

fs.futimesSync(fd, atime, mtime)

 

同步磁盘缓存

fs.fsync(fd, [callback(err)])

fs.fsyncSync(fd)

                                                                                                        

 

1.2.4    HTTP 服务器与客户端

Node.js 标准库提供了 http 模块,其中封装了一个高效的HTTP 服务器和一个简易的HTTP 客户端。http.Server 是一个基于事件的 HTTP 服务器,它的核心由 Node.js 下层 C++部分实现,而接口由 JavaScript 封装,兼顾了高性能与简易性。http.request 则是一个HTTP 客户端工具,用于向 HTTP服务器发起请求,例如实现Pingback或者内容抓取。

1.2.4.1        HTTP 服务器

http.Server 是 http 模块中的 HTTP 服务器对象,用 Node.js 做的所有基于 HTTP 协议的系统,如网站、社交应用甚至代理服务器,都是基于http.Server 实现的。它提供了一套封装级别很低的 API,仅仅是流控制和简单的消息解析,所有的高层功能都要通过它的接口来实现。

http.createServer 创建了一个 http.Server 的实例,将一个函数作为 HTTP 请求处理函数。这个函数接受两个参数,分别是请求对象( req )和响应对象( res )。

1.2.4.1.1                 http.Server 的事件

http.Server 是一个基于事件的 HTTP 服务器,所有的请求都被封装为独立的事件,开发者只需要对它的事件编写响应函数即可实现 HTTP 服务器的所有功能。它继承自EventEmitter,提供了以下几个事件。

request:当客户端请求到来时,该事件被触发,提供两个参数 req 和res,分别是http.ServerRequest 和 http.ServerResponse 的实例,表示请求和响应信息。

connection:当 TCP 连接建立时,该事件被触发,提供一个参数 socket,为net.Socket 的实例。connection 事件的粒度要大于 request,因为客户端在Keep-Alive 模式下可能会在同一个连接内发送多次请求。

close :当服务器关闭时,该事件被触发。注意不是在用户连接断开时。

除此之外还有checkContinue、upgrade、clientError 事件,通常我们不需要关心,只有在实现复杂的 HTTP 服务器的时候才会用到。

在这些事件中, 最常用的就是 request 了, 因此 http 提供了一个捷径:http.createServer([requestListener]), 功能是创建一个 HTTP 服务器并将requestListener作为 request 事件的监听函数,

1.2.4.1.2                 http.ServerRequest

http.ServerRequest 是 HTTP 请求的信息,是后端开发者最关注的内容。它一般由http.Server 的 request 事件发送,作为第一个参数传递,通常简称 request 或 req。

ServerRequest 提供一些属性,下表列出了这些属性。

HTTP 请求一般可以分为两部分:请求头(Request Header)和请求体(Requset Body)。以上内容由于长度较短都可以在请求头解析完成后立即读取。而请求体可能相对较长,需要一定的时间传输,因此 http.ServerRequest 提供了以下3个事件用于控制请求体传输。

data :当请求体数据到来时,该事件被触发。该事件提供一个参数 chunk,表示接收到的数据。如果该事件没有被监听,那么请求体将会被抛弃。该事件可能会被调用多次。

ServerRequest 的属性

名 称

含 义

complete

客户端请求是否已经发送完成

httpVersion

HTTP 协议版本,通常是 1.0 或 1.1

method

HTTP 请求方法,如 GET、POST、PUT、DELETE 等

url

原始的请求路径,例如 /static/image/x.jpg 或 /user?name=byvoid

 

headers

HTTP 请求头

trailers

HTTP 请求尾(不常见)

connection

当前 HTTP 连接套接字,为 net.Socket 的实例

socket

connection 属性的别名

client

client 属性的别名

 

 

 

1.2.4.1.3                 获取 GET 请求内容

注意,http.ServerRequest提供的属性中没有类似于PHP 语言中的 $_GET 或$_POST 的属性,那我们如何接受客户端的表单请求呢?Node.js 的 url 模块中的 parse 函数提供了这个功能。

通过 url.parse,原始的 path 被解析为一个对象,其中 query 就是我们所谓的 GET请求的内容,而路径则是 pathname。

1.2.4.1.4                 获取 POST 请求内容

HTTP 协议 1.1 版本提供了8种标准的请求方法,其中最常见的就是 GET 和 POST。相比GET 请求把所有的内容编码到访问路径中,POST 请求的内容全部都在请求体中。

http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作,譬如上传文件。而很多时候我们可能并不需要理会请求体的内容,恶意的 POST请求会大大消耗服务器的资源。所以 Node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做。

1.2.4.1.5                 http.ServerResponse

http.ServerResponse 是返回给客户端的信息,决定了用户最终能看到的结果。它也是由http.Server 的 request 事件发送的,作为第二个参数传递,一般简称为response 或 res。

http.ServerResponse 有三个重要的成员函数,用于返回响应头、响应内容以及结束请求。

response.writeHead(statusCode, [headers]):向请求的客户端发送响应头。

statusCode 是 HTTP 状态码,如 200 (请求成功)、404 (未找到)等。headers是一个类似关联数组的对象,表示响应头的每个属性。该函数在一个请求内最多只能调用一次,如果不调用,则会自动生成一个响应头。

response.write(data, [encoding]):向请求的客户端发送响应内容。data 是一个 Buffer 或字符串,表示要发送的内容。如果 data 是字符串,那么需要指定encoding 来说明它的编码方式,默认是 utf-8。在 response.end 调用之前,response.write 可以被多次调用。

response.end([data], [encoding]):结束响应,告知客户端所有发送已经完成。当所有要返回的内容发送完毕的时候,该函数 必须 被调用一次。它接受两个可选参数,意义和 response.write 相同。如果不调用该函数,客户端将永远处于等待状态。

1.2.4.2        HTTP 客户端

http 模块提供了两个函数 http.request 和 http.get,功能是作为客户端向 HTTP服务器发起请求。

http.request(options, callback) 发起 HTTP 请求。接受两个参数,option 是一个类似关联数组的对象,表示请求的参数,callback 是请求的回调函数。option常用的参数如下所示。

 host :请求网站的域名或 IP 地址。

 port :请求网站的端口,默认 80。

 method :请求方法,默认是 GET。

 path :请求的相对于根的路径,默认是“/”。QueryString 应该包含在其中。例如 /search?query=byvoid。

 headers :一个关联数组对象,为请求头的内容。

callback 传递一个参数,为 http.ClientResponse 的实例。http.request 返回一个 http.ClientRequest 的实例。

不要忘了通过req.end() 结束请求,否则服务器将不会收到信息。

http.get(options, callback) http 模块还提供了一个更加简便的方法用于处理GET请求:http.get。它是 http.request 的简化版,唯一的区别在于http.get自动将请求方法设为了 GET 请求,同时不需要手动调用 req.end()。

1.2.4.2.1                 http.ClientRequest

http.ClientRequest 是由 http.request 或 http.get 返回产生的对象,表示一个已经产生而且正在进行中的 HTTP 请求。它提供一个 response 事件,即 http.request或 http.get 第二个参数指定的回调函数的绑定对象。

http.ClientRequest 像 http.ServerResponse 一样也提供了 write 和 end 函数,用于向服务器发送请求体,通常用于 POST、PUT 等操作。所有写结束以后必须调用 end函数以通知服务器,否则请求无效。http.ClientRequest 还提供了以下函数。

request.abort():终止正在发送的请求。

request.setTimeout(timeout, [callback]):设置请求超时时间,timeout 为毫秒数。当请求超时以后,callback 将会被调用。

此外还有request.setNoDelay([noDelay])、request.setSocketKeepAlive([enable],[initialDelay]) 等函数。

1.2.4.2.2                 http.ClientResponse

http.ClientResponse 与 http.ServerRequest 相似,提供了三个事件 data、end和 close,分别在数据到达、传输结束和连接结束时触发,其中 data 事件传递一个参数chunk,表示接收到的数据。

http.ClientResponse 也提供了一些属性,用于表示请求的结果状态,参见下表。

ClientResponse的属性

名 称

含 义

statusCode

HTTP 状态码,如 200、404、500

 

httpVersion

HTTP 协议版本,通常是 1.0 或 1.1

headers

HTTP 请求头

trailers

HTTP 请求尾(不常见)

 

http.ClientResponse 还提供了以下几个特殊的函数。

response.setEncoding([encoding]):设置默认的编码,当 data 事件被触发时,数据将会以 encoding 编码。默认值是 null,即不编码,以 Buffer 的形式存储。常用编码为 utf8。

response.pause():暂停接收数据和发送事件,方便实现下载功能。

response.resume():从暂停的状态中恢复。

 

1.3     实践

1.3.1    路由控制

1.3.1.1        工作原理

routes 是一个文件夹形式的本地模块,即./routes/index.js,它的功能是为指定路径组织返回内容,相当于 MVC 架构中的控制器。通过express.createServer()函数创建了一个应用的实例,后面的所有操作都是针对于这个实例进行的。

接下来是三个app.configure 函数,分别指定了通用、开发和产品环境下的参数。

第一个app.configure 直接接受了一个回调函数,后两个则只能在开发和产品环境中调用。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 是错误控制器。

由 Express 创建的网站架构如下图所示。


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

1.3.1.2        REST 风格的路由规则

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

 GET:请求获取指定资源。

 HEAD:请求指定资源的响应头。

 POST:向指定资源提交数据。

 PUT:请求服务器存储一个资源。

 DELETE:请求服务器删除指定资源。

 TRACE:回显服务器收到的请求,主要用于测试或诊断。

 CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。

 OPTIONS:返回服务器支持的HTTP请求方法。

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

 GET:获取

 POST:新增

 PUT:更新

 DELETE:删除

Express 对每种 HTTP 请求方法都设计了不同的路由绑定函数,例如前面例子全部是app.get,表示为该路径绑定了 GET 请求,向这个路径发起其他方式的请求不会被响应。下表是 Express 支持的所有 HTTP 请求的绑定函数。

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)

 

例如我们要绑定某个路径的POST 请求,则可以用app.post(path, callback) 的方法。需要注意的是 app.all 函数,它支持把所有的请求方式绑定到同一个响应函数,是一个非常灵活的函数。

1.3.1.3        控制权转移

Express 支持同一路径绑定多个路由响应函数。但当你访问任何被这两条同样的规则匹配到的路径时,会发现请求总是被前一条路由规则捕获,后面的规则会被忽略。原因是 Express 在处理路由规则时,会优先匹配先定义的路由规则,因此后面相同的规则被屏蔽。

Express 提供了路由控制权转移的方法,即回调函数的第三个参数next,通过调用next(),会将路由控制权转移给后面的规则。

先被第一条路由规则捕获,然后使用 next() 转移控制权,又被第二条规则捕获,向浏览器返回了信息。这是一个非常有用的工具,可以让我们轻易地实现中间件,而且还能提高代码的复用程度。

1.3.2    模板引擎

在 MVC 架构中,模板引擎包含在服务器端。控制器得到用户请求后,从模型获取数据,

调用模板引擎。模板引擎以数据和页面模板为输入,生成 HTML 页面,然后返回给控制器,

由控制器交回客户端。下图是模板引擎在 MVC 架构中的示意图。

 

1.3.2.1        使用模板引擎

在 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 目录下的模板文件名,不包含文件的扩展名;第二个参数是传递给模板的数据,用于模板翻译。

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

 <% code %>:JavaScript 代码。

 <%= code %>:显示替换过 HTML 特殊字符的内容。

 <%- code %>:显示原始 HTML 内容。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值