不知不觉,已经过去快一个星期没有继续更新了,中秋假日总会使人想安于现状,这一章继续上面使用 Node.js 进行 Web 开发,介绍模板引擎,尝试建立一个微博网站,实现用户注册登录和发表微博的基础功能。好了,让我们一起开始吧
使用 Node.js 进行 Web 开发(中)
模板引擎
Express 的路由控制,是网络架构最核心的部分,即 MVC 架构中的控制器。而视图,主要是通过模板引擎的使用和集成来实现。视图决定了用户最终看到的东西,也是最重要的部分,这里以 ejs 为例介绍模板引擎的使用方法。
什么是模板引擎
模板引擎(Template Engine) 是一个从页面模板根据一定的规则生成的 HTML 的工具。按照这种模式,整个网站就由一个个页面模块组成,所有的逻辑都嵌入在模块中,这种模式大大降低动态网页开发的门槛,但随着规模的扩大会遇到许多问题。
- 页面功能逻辑与页面布局样式耦合,网站模式变大以后逐渐难以维护
- 语法复杂,对于非技术的网页设计者来说门槛较高,难以学习。
- 功能过于全面,页面设计者可以在页面上编程,不利于功能划分,也使模板解析效率降低。
这些问题制约了早期模板引擎的发展,直到 MVC 开发模式普及,模板引擎才开始遍地开花。现代的模板引擎是 MVC 的一部分,在功能划分上它严格属于视图部分,因此功能以生存 HTML 页面为核心,不会引入过多的编程语言的功能。
模板引擎的功能主要是将页面模块和要显示的数据结合起来生成 HTML 页面。它既可以运行在服务器端又可以运行在客户端,大多数时候它都在服务器端直接被解析成 HTML ,解析完成后再传输给客户端,因为客户端甚至无法判断页面是否是模板引擎生成的。有时候模板引擎也可以而运行在客户端,即浏览器中,典型的代表就是 XSLT,它以 XML 为输入,在客户端生成 HTML 页面。但是由于浏览器兼容性问题,XSLT 并不是很流行。目前的主流还是由服务器运行模板引擎。
在MVC架构中,模板引擎包含在服务器端。控制器得到用户请求后,从模型获取数据,调用模板引擎。模板引擎以数据和页面模板输入,生成 HTML 页面,然后返回控制器,由控制器交给客户端。
使用模板引擎
基于 JavaScript 的模板引擎有许多种实现。推荐使用 ejs (Embedded JavaScript),因为它十分简单而且与 Express 集成良好。由于它是标准 JavaScript 实现的,因此它不仅可以运行在服务器端,还可以运行在浏览器中。
app.js 中通过以下两个语句设置了模板引擎和页面模板的位置:
1 | // view engine setup |
表明要使用的模板引擎是 ejs ,页面模板在 views 子目录下。在 routes/index.js 用一下语句,绑定路由和调用模板引擎。
1 | var express = require('express'); |
res.render
的功能是调用模板引擎,并将其产生的页面直接返回给客户端。它接受两个参数,第一个是模板的名称,即 views
目录下的模板文件名,不包含文件的扩展名;第二个参数是传递给模板的数据,用于模板翻译。index.ejs 内容如下:
1 | <h1><%= title %></h1> |
上面代码其中有两处 <%= title %>
,用于模板变量显示,它们在模板翻译时会被替换成 Express,因为 res.render
传递了 { title: 'Express' }
。
ejs 的标签系统非常简单,它只有以下3种标签。
<% code %>
:JavaScript 代码。<%= code %>
:显示替换过 HTML 特殊字符的内容。<%- code %>
:显示原始 HTML 内容。
可以用它们实现页面模板系统能实现的任何内容。
页面布局
views/index.ejs 里面的代码
1 | <!DOCTYPE html> |
Express 可以自动套用 layout.ejs ,所以可以将 index.ejs 里面的代码分开
1 | <!DOCTYPE html> |
layout.ejs
是一个页面布局模板,它描述了整个页面的框架结构,默认情况下每个单独的页面都继承自这个框架,替换掉 <%- body %>
部分。这个功能通常非常有用,因为一般为了保持整个网站的一致风格,HTML 页面的 <head>
部分以及页眉页脚中的大量内容是重复的,因此我们可以把它们放在 layout.ejs
中。当然,这个功能并不是强制的,如果想关闭它,可以在 app.js 的中 app.configure
中添加以下内容,这样页面布局功能就被关闭了。
1 | app.set('view options', { |
另一种情况是,一个网站可能需要不止一种页面布局,例如网站分前台展示和后台管理系统,两者的页面结构有很大的区别,一套页面布局不能满足需求。这时我们可以在页面模板翻译时指定页面布局,即设置 layout 属性,例如:
1 | function(req, res) { |
这段代码会在翻译 userlist 页面模板时套用 admin.ejs 作为页面布局。
片段视图
Express 的视图系统还支持片段视图(partials),它就是一个页面的片段,通常是重复的内容,用于迭代显示。它就是一个页面的片段,通常是重复的内容,用于迭代显示。通过它你可以将相对独立的页面块分割出去,而且可以避免显式地使用 for 循环。而其实在 Express4.0以后,已经开始用 include
来取代 partials
了。
如果仍要用的话,可以采用以下步骤:
- 运行cmd 输入:npm install express-partials -g
- 下载成功后.在app.js 中引用此插件 var partials = require(‘express-partials’);
- 开启此插件, 在app.js 中 app.set(‘view engine’, ‘ejs’); 代码后添加如下代码: app.use(partials());
不过这里,我还是用新的方法,首先在 routers/index.js 中新增以下内容。
1 | router.get('/list', function (req, res, next) { |
接着在 views目录下新建 list.ejs 内容是
1 | <ul> |
同时建立 listitem.ejs 内容是
1 | <li><%= listitem %></li> |
访问 http://localhost:3000/list,可以在源代码中看到以下内容:
1 | <html> |
视图助手
Express 提供了一种叫做视图助手的工具,它的功能是允许在视图中访问一个全局的函数或对象,不用每次调用视图解析的时候单独传入。前面提到的 partial 就是一个视图助手。
视图助手有两类,分别是静态视图助手和动态视图助手。这两者的差别在于,静态视图助手可以是任何类型的对象,包括接受任意参数的函数,但访问到的对象必须是与用户请求无关的,一般指的是项目的名称,地址等配置参数或者是公共的方法,这些变量,方法只能用在模板视图里面。动态视图助手指的是该视图变量,方法与请求有关,一般用来解析请求信息,如用户登录信息,请求地址等。
静态视图助手可以通过 app.helpers() 函数注册,它接受一个对象,对象的每个属性名称为视图助手的名称,属性值对应视图助手的值。动态视图助手则通过 app.dynamicHelpers() 注册,方法与静态视图助手相同,但每个属性的值必须为一个函数,该函数提供 req 和 res 。当然这是express4.0之前的用法,现在视图助手有所不一样了。
静态视图
在 app.js 中添加
1 | // app 静态视图助手 |
新建一个 helper.ejs,内容如下:
1 | <h1><%= appName%></h1> |
访问 http://localhost:3000/helper 可以看到下面内容
动态视图
动态视图助手的实现方式和路由的方式相似,所以动态视图助手要将语句放在路由的前面
在 routers/index.js 里面添加以下内容
1 | // 动态视图助手 |
然后在 helper.ejs 添加以下内容
1 | <p><%= appUrl %></p> |
刷新网页,可以看到以下内容
视图助手的本质其实就是给所有视图注册了全局变量,因此无需每次在调用模板引擎时传递数据对象。
接下来 在 app.js 中加入
1 | var util = require('util'); |
然后在 routers/index.js 添加
1 | // 动态视图助手 |
同样在 helper.ejs 添加
1 | <p><%=inspect(headers)%></p> |
重开服务器,可以看到
在后面使用 session 时会发现它是非常有用的。
建立微博网站
功能分析
首先,微博应该以用户为中心,因
此需要有用户的注册和登录功能。微博网站最核心的功能是信息的发表,这个功能涉及许多方面,包括数据库访问、前端显示等。一个完整的微博系统应该支持信息的评论、转发、圈点用户等功能,但出于演示目的,我们不能一一实现所有功能,只是实现一个微博社交网站的雏形。
路由规划
在完成功能设计以后,下一个要做的事情就是路由规划了。路由规划,或者说控制器规划是整个网站的骨架部分,因为它处于整个架构的枢纽位置,相当于各个接口之间的粘合剂,所以应该优先考虑。
根据功能设计,我们把路由按照以下方案规划。
/
:首页/u/[user]
:用户的主页/post
:发表信息/reg
:用户注册/login
:用户登录/logout
:用户登出
以上页面还可以根据用户状态细分。发表信息以及用户登出页面必须是已登录用户才能操作的功能,而用户注册和用户登入所面向的对象必须是未登入的用户。首页和用户主页则针对已登入和未登入的用户显示不同的内容。
修改 routers/index.js 的内容
1 | // 正式微博路由 |
由于/reg
以及 /login
接受表单信息的同时还要显示用户注册要填写的表单,所以使用 router.route
方法链式将 post/get 方法写在了一起。
界面设计
Twitter Bootstrap 是由 Twitter 的设计师和工程师发起的开源项目,它提供了一套与 Twitter 风格一致的简洁、优雅的 Web UI,包含了完全由 HTML、CSS、JavaScript 实现的用户交互工具。可以轻松地使用Twitter Bootstrap 制作出优美的界面。
使用 Bootstrap
从http://twitter.github.com/bootstrap/下载bootstrap.zip,解压后可以看到以下文件:
1 | css/bootstrap-responsive.css |
其中所有的 JavaScript 和 CSS 文件都提供了开发版和产品版,前者是原始的代码,后者经过压缩,文件名中带有 min。将 img 目录复制到工程 public 目录下,将 bootstrap.css、bootstrap-responsive.css 复制到 public/stylesheets 中,将 bootstrap.js 复制到 public/javascripts 目录中,然后从http://jquery.com/下载一份最新版的 jquery.js 也放入 public/javascripts 目录中。
由于 express4.x版本以及不再支持 layout.ejs,而是使用 include
首先我们本来要插入layout.ejs,内容如下:
1 |
|
上面代码是使用 Bootstrap部件实现的一个简单页面框架,整个页面分为顶部工具栏、正文和页脚三部分,其中正文和页脚包含在名为 container 的 div 标签中。
然后我们新建两个ejs 文件,分别是 footer.ejs 和 header.ejs
1 | // header.ejs |
1 | // footer.ejs |
然后,在 index.ejs 添加以下内容
1 | <% include header.ejs %> |
访问 http://localhost:3000/ ,就可以看到以下内容
不知不觉,又篇幅有点长了,后面的是最后一章,将会介绍用户注册和登录已经发表微博的基本功能,希望可以在下一章一起学习。