Node.js【6】Web开发、进阶(模块加载、控制流、部署、弊端)
笔记来自《Node.js开发指南》BYVoid编著
实现过程:https://github.com/ichenxiaodao/express-example
第5章 使用Node.js进行Web开发
从零开始用Node.js实现一个微博系统,功能包括路由控制、页面模板、数据库访问、用户注册、登录、用户会话等内容。
会介绍Express框架、MVC设计模式、ejs模板引擎以及MongoDB数据库的操作。
5.1、准备工作
Express(http://expressjs.com/)除了为http模块提供了更高层的接口外,还实现了许多功能,其中包括:
- 路由控制;
- 模板解析支持;
- 动态视图;
- 用户会话;
- CSRF保护;
- 静态文件服务;
- 错误控制器;
- 访问日志;
- 缓存;
- 插件支持。
它只是一个轻量级的Web架,多数功能只是对HTTP协议中常用操作的封装,更多的功能需要插件或者整合其他模块来完成。
5.2、快速开始
安装Express:
npm install -g [email protected]
npm install -g express-generator(v4.0.0以后)
ejs(EmbeddedJavaScript)是一个标签替换引擎,其语法与ASP、PHP相似,易于学习,目前被广泛应用。Express默认提供的引擎是jade,它颠覆了传统的模板引擎,制定了一套完整的语法用来生成HTML的每个标签结构,功能强大但不易学习。
建立网站基本结构:
express -t ejs microblog
cd microblog && npm install
无参数的npm install的功能就是检查当前目录下的package.json,并自动安装所有指定的依赖。
5.3、路由控制
【图】Express网站的架构
这是一个典型的MVC架构,浏览器发起请求,由路由控制器接受,根据不同的路径定向到不同的控制器。控制器处理用户的具体请求,可能会访问数据库中的对象,即模型部分。控制器还要访问模板引擎,生成视图的HTML,最后再由控制器返回给浏览器,完成一次请求。
Express支持REST风格的请求方式,在介绍之前我们先说明一下什么是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:删除
【图】REST风格HTTP请求的特点
Express对每种HTTP请求方法都设计了不同的路由绑定函数,例如前面例子全部是app.get,表示为该路径绑定了GET请求,向这个路径发起其他方式的请求不会被响应。
【图】Express支持的HTTP请求的绑定函数
Express支持同一路径绑定多个路由响应函数。但当你访问任何被这两条同样的规则匹配到的路径时,会发现请求总是被前一条路由规则捕获,后面的规则会被忽略。原因是Express在处理路由规则时,会优先匹配先定义的路由规则,因此后面相同的规则被屏蔽。
Express提供了路由控制权转移的方法,即回调函数的第三个参数next,通过调用next(),会将路由控制权转移给后面的规则。
5.4、模板引擎
模板引擎(Template Engine)是一个从页面模板根据一定的规则生成HTML的工具。PHP堪称是最早的模板引擎的雏形。随后的ASP、JSP都沿用了这个模式,即建立一个HTML页面模板,插入可执行的代码,运行时动态生成HTML。
在MVC架构中,模板引擎包含在服务器端。控制器得到用户请求后,从模型获取数据,调用模板引擎。模板引擎以数据和页面模板为输入,生成HTML页面,然后返回给控制器,
由控制器交回客户端。
基于JavaScript的模板引擎有许多种实现,我们推荐使用ejs(Embedded JavaScript),因为它十分简单,而且与Express集成良好。
ejs的标签系统非常简单,它只有以下3种标签。
- <% code %>:JavaScript代码。
- <%= code %>:显示替换过HTML特殊字符的内容。
- <%- code %>:显示原始HTML内容。
Express提供了一种叫做视图助手的工具,它的功能是允许在视图中访问一个全局的函数或对象,不用每次调用视图解析的时候单独传入。
视图助手有两类,分别是静态视图助手和动态视图助手。这两者的差别在于,静态视图助手可以是任何类型的对象,包括接受任意参数的函数,但访问到的对象必须是与用户请求无关的,而动态视图助手只能是一个函数,这个函数不能接受参数,但可以访问req和res对象。
视图助手的本质其实就是给所有视图注册了全局变量,因此无需每次在调用模板引擎时
传递数据对象。
5.5、建立微博网站
功能分析、路由规划、界面设计、使用Bootstrap
5.6、用户注册和登陆
MongoDB是一个对象数据库,它没有表、行等概念,也没有固定的模式和结构,所有的数据以文档的形式存储。所谓文档就是一个关联数组式的对象,它的内部由属性组成,一个属性对应的值可能是一个数、字符串、日期、数组,甚至是一个嵌套的文档。
MongoDB的数据格式就是JSON。准确地说,MongoDB的数据格式是BSON(Binary JSON),它是JSON的一个扩展。
会话是一种持久的网络协议,用于完成服务器和客户端之间的一些交互行为。会话是一个比连接粒度更大的概念,一次会话可能包含多次连接,每次连接都被认为是会话的一次操作。在网络应用开发中,有必要实现会话以帮助用户交互。
为了在无状态的HTTP协议之上实现会话,Cookie诞生了。Cookie是一些存储在客户端的信息,每次连接的时候由浏览器向服务器递交,服务器也向浏览器发起存储Cookie的请求,依靠这样的手段服务器可以识别客户端。我们通常意义上的HTTP会话功能就是这样实现的。具体来说,浏览器首次向服务器发起请求时,服务器生成一个唯一标识符并发送给客户端浏览器,浏览器将这个唯一标识符存储在Cookie中,以后每次再发起请求,客户端浏览器都会向服务器传送这个唯一标识符,服务器通过这个唯一标识符来识别用户。
5.7、发表微博
略第6章 Node.js进阶话题
6.1、模块加载机制
Node.js的模块可以分为两大类,一类是核心模块,另一类是文件模块。核心模块就是Node.js标准API中提供的模块,如fs、http、net、vm等,这些都是由Node.js官方提供的模块,编译成了二进制代码。我们可以直接通过require获取核心模块,例如require(‘fs‘)。核心模块拥有最高的加载优先级,换言之如果有模块与其命名冲突,Node.js总是会加载核心模块。
文件模块则是存储为单独的文件(或文件夹)的模块,可能是JavaScript代码、JSON或编译好的C/C++代码。文件模块的加载方法相对复杂,但十分灵活,尤其是和npm结合使用时。在不显式指定文件模块扩展名的时候,Node.js会分别试图加上.js、.json和.node扩展名。.js是JavaScript代码,.json是JSON格式的文本,.node是编译好的C/C++代码。
文件模块加载优先级:.js->.json->.node
文件模块的加载有两种方式,一种是按路径加载,一种是查找node_modules文件夹。
Node.js模块不会被重复加载,这是因为Node.js通过文件名缓存所有加载过的文件模块,所以以后再访问到时就不会重新加载了。
总结一下使用require(some_module)时的加载顺序。
(1)如果some_module是一个核心模块,直接加载,结束。
(2)如果some_module以“/”、“./”或“../”开头,按路径加载some_module,结束。
(3)假设当前目录为current_dir,按路径加载current_dir/node_modules/some_module。
如果加载成功,结束。
如果加载失败,令current_dir为其父目录。
重复这一过程,直到遇到根目录,抛出异常,结束。
6.2、控制流
var fs = require('fs'); var files = ['a.txt', 'b.txt', 'c.txt']; for (var i = 0; i < files.length; i++) { fs.readFile(files[i], 'utf-8', function(err, contents) { console.log(files[i] + ": " + contents); }); } /* 运行结果: undefined: AAA undefined: BBB undefined: CCC */原因是3次读取文件的回调函数事实上是同一个实例,其中引用到的i值是上面循环执行结束后的值,因此不能分辨。
解决方法:
var fs = require('fs'); var files = ['a.txt', 'b.txt', 'c.txt']; files.forEach(function(filename) { fs.readFile(filename, 'utf-8', function(err, contents) { console.log(filename + ": " + contents); }); }); /* 运行结果: a.txt: AAA b.txt: BBB c.txt: CCC */除了循环的陷阱,Node.js异步式编程还有一个显著的问题,即深层的回调函数嵌套。
许多项目试图解决这一难题。async是一个控制流解耦模块,它提供了async.series、async.parallel、async.waterfall等函数,在实现复杂的逻辑时使用这些函数代替回调函数嵌套可以让程序变得更清晰可读且易于维护。
还有streamlinejs、jscex、eventproxy。
6.3、Node.js应用部署
在部署Node.js应用的时候一定要考虑到故障恢复,提高系统的可靠性。
有必要实现日志功能。
使用多进程来提高系统的性能。
反向代理来实现基于域名的端口共享。
通过脚本实现启动或停止服务器的功能。
6.4、Node.js不是银弹
无论使用什么语言、工具,所能改变的仅仅是开发的舒适程度和方便程度,而最终软件的好坏所能改变的范围相当有限。任何试图以限制程序员犯错来提高软件质量的方式最终都已经以失败告终。真正优秀的软件是靠优秀的程序员开发出来的,优秀的语言、平台、工具只有在优秀的程序员的手中才能显现出它的威力。
Node.js不适合做的事情:
- 计算密集型的程序;
- 单用户多任务型应用;
- 逻辑十分复杂的事务;
- Unicode与国际化;
文档信息
- 版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
- 最后修改时间:2014年07月14日 00:08
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。