我们使用 jQuery + Semantic-UI 实现前端页面的设计,最终效果图如下:
注册页

登录页

未登录时的主页(或用户页)

登录后的主页(或用户页)

发表文章页

编辑文章页

未登录时的文章页

登录后的文章页

通知



4.5.1 组件
前面提到过,我们可以将模板拆分成一些组件,然后使用 ejs 的 include 方法将组件组合起来进行渲染。我们将页面切分成以下组件:
主页

文章页

根据上面的组件切分图,我们创建以下样式及模板文件:
public/css/style.css
/* ---------- 全局样式 ---------- */body {width: 1100px;height: 100%;margin: 0 auto;padding-top: 40px;}a:hover {border-bottom: 3px solid #4fc08d;}.button {background-color: #4fc08d !important;color: #fff !important;}.avatar {border-radius: 3px;width: 48px;height: 48px;float: right;}/* ---------- nav ---------- */.nav {margin-bottom: 20px;color: #999;text-align: center;}.nav h1 {color: #4fc08d;display: inline-block;margin: 10px 0;}/* ---------- nav-setting ---------- */.nav-setting {position: fixed;right: 30px;top: 35px;z-index: 999;}.nav-setting .ui.dropdown.button {padding: 10px 10px 0 10px;background-color: #fff !important;}.nav-setting .icon.bars {color: #000;font-size: 18px;}/* ---------- post-content ---------- */.post-content h3 a {color: #4fc08d !important;}.post-content .tag {font-size: 13px;margin-right: 5px;color: #999;}.post-content .tag.right {float: right;margin-right: 0;}.post-content .tag.right a {color: #999;}
views/header.ejs
<!DOCTYPE html><html><head><meta charset="utf-8"><title><%= blog.title %></title><link rel="stylesheet" href="//cdn.bootcss.com/semantic-ui/2.1.8/semantic.min.css"><link rel="stylesheet" href="/css/style.css"><script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script><script src="//cdn.bootcss.com/semantic-ui/2.1.8/semantic.min.js"></script></head><body><%- include('components/nav') %><%- include('components/nav-setting') %><%- include('components/notification') %>
views/footer.ejs
<script type="text/javascript">$(document).ready(function () {// 点击按钮弹出下拉框$('.ui.dropdown').dropdown();// 鼠标悬浮在头像上,弹出气泡提示框$('.post-content .avatar-link').popup({inline: true,position: 'bottom right',lastResort: 'bottom right'});})</script></body></html>
注意:上面
<script></script>是 semantic-ui 操控页面控件的代码,一定要放到 footer.ejs 的</body>的前面,因为只有页面加载完后才能通过 JQuery 获取 DOM 元素。
在 views 目录下新建 components 目录用来存放组件(即可以复用的模板片段),在该目录下创建以下文件:
views/components/nav.ejs
<div class="nav"><div class="ui grid"><div class="four wide column"></div><div class="eight wide column"><a href="/posts"><h1><%= blog.title %></h1></a><p><%= blog.description %></p></div></div></div>
views/components/nav-setting.ejs
<div class="nav-setting"><div class="ui buttons"><div class="ui floating dropdown button"><i class="icon bars"></i><div class="menu"><% if (user) { %><a class="item" href="/posts?author=<%= user._id %>">个人主页</a><div class="divider"></div><a class="item" href="/posts/create">发表文章</a><a class="item" href="/signout">登出</a><% } else { %><a class="item" href="/signin">登录</a><a class="item" href="/signup">注册</a><% } %></div></div></div></div>
views/components/notification.ejs
<div class="ui grid"><div class="four wide column"></div><div class="eight wide column"><% if (success) { %><div class="ui success message"><p><%= success %></p></div><% } %><% if (error) { %><div class="ui error message"><p><%= error %></p></div><% } %></div></div>
4.5.2 app.locals 和 res.locals
上面的 ejs 模板中我们用到了 blog、user、success、error 变量,我们将 blog 变量挂载到 app.locals 下,将 user、success、error 挂载到 res.locals 下。为什么要这么做呢?app.locals 和 res.locals 是什么?它们有什么区别?
express 中有两个对象可用于模板的渲染:app.locals 和 res.locals。我们从 express 源码一探究竟:
express/lib/application.js
app.render = function render(name, options, callback) {...var opts = options;var renderOptions = {};...// merge app.localsmerge(renderOptions, this.locals);// merge options._localsif (opts._locals) {merge(renderOptions, opts._locals);}// merge optionsmerge(renderOptions, opts);...tryRender(view, renderOptions, done);};
express/lib/response.js
res.render = function render(view, options, callback) {var app = this.req.app;var opts = options || {};...// merge res.localsopts._locals = self.locals;...// renderapp.render(view, opts, done);};
可以看出:在调用 res.render 的时候,express 合并(merge)了 3 处的结果后传入要渲染的模板,优先级:res.render 传入的对象> res.locals 对象 > app.locals 对象,所以 app.locals 和 res.locals 几乎没有区别,都用来渲染模板,使用上的区别在于:app.locals 上通常挂载常量信息(如博客名、描述、作者这种不会变的信息),res.locals 上通常挂载变量信息,即每次请求可能的值都不一样(如请求者信息,res.locals.user = req.session.user)。
修改 index.js,在 routes(app) 上一行添加如下代码:
// 设置模板全局常量app.locals.blog = {title: pkg.name,description: pkg.description}// 添加模板必需的三个变量app.use(function (req, res, next) {res.locals.user = req.session.userres.locals.success = req.flash('success').toString()res.locals.error = req.flash('error').toString()next()})
这样在调用 res.render 的时候就不用传入这四个变量了,express 为我们自动 merge 并传入了模板,所以我们可以在模板中直接使用这四个变量。
