我们使用 jQuery + Semantic-UI 实现前端页面的设计,最终效果图如下:
**注册页**
![](https://box.kancloud.cn/9fd6acfca391de626095ef18698a4212_1366x768.png)
**登录页**
![](https://box.kancloud.cn/eefb4dd589e2f27ceeb363c005450ea1_1366x768.png)
**未登录时的主页(或用户页)**
![](https://box.kancloud.cn/edd9fa55198c2c2f480e2ae5d9d81857_1366x768.png)
**登录后的主页(或用户页)**
![](https://box.kancloud.cn/c42c5f7b36abe3c4a38723ba6894474f_1366x768.png)
**发表文章页**
![](https://box.kancloud.cn/d6b416ef995abe1b8b55a09a319458b1_1366x768.png)
**编辑文章页**
![](https://box.kancloud.cn/b677ffc5b8ec551afcaedc25fd622e55_1366x768.png)
**未登录时的文章页**
![](https://box.kancloud.cn/fb79b800e51af1795fb1cd762be407f6_1366x768.png)
**登录后的文章页**
![](https://box.kancloud.cn/ac53377350b6474db185c7137d99c4a9_1366x850.png)
**通知**
![](https://box.kancloud.cn/7b9720ef3be40dbd28c967ef7754aa7f_1366x850.png)
![](https://box.kancloud.cn/2f05f57520b2b6ff9611b22441ed25d7_1366x768.png)
![](https://box.kancloud.cn/1a32ce79d5744c8a09f39f3da3aab07d_1366x768.png)
## 4.5.1 组件
前面提到过,我们可以将模板拆分成一些组件,然后使用 ejs 的 include 方法将组件组合起来进行渲染。我们将页面切分成以下组件:
**主页**
![](https://box.kancloud.cn/a3b39704355184290340f60999195429_1366x852.png)
**文章页**
![](https://box.kancloud.cn/bb50454aa8b2ce0a21101131352196f7_1366x850.png)
根据上面的组件切分图,我们创建以下样式及模板文件:
**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">
// 点击按钮弹出下拉框
$('.ui.dropdown').dropdown();
// 鼠标悬浮在头像上,弹出气泡提示框
$('.post-content .avatar').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
上面的模板中我们用到了 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.locals
merge(renderOptions, this.locals);
// merge options._locals
if (opts._locals) {
merge(renderOptions, opts._locals);
}
// merge options
merge(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.locals
opts._locals = self.locals;
...
// render
app.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.user;
res.locals.success = req.flash('success').toString();
res.locals.error = req.flash('error').toString();
next();
});
```
这样在调用 `res.render` 的时候就不用传入这四个变量了,express 为我们自动 merge 并传入了模板,所以我们可以在模板中直接使用这四个变量。
- 使用 Express + MongoDB 搭建多人博客
- 1.1 Node.js 的安装与使用
- 1.2 MongoDB 的安装与使用
- 2.1 require
- 2.2 exports 和 module.exports
- 2.3 Promise
- 2.4 环境变量
- 2.5 package.json
- 2.6 npm 使用注意事项
- 3.1 初始化一个 Express 项目
- 3.2 路由
- 3.3 模板引擎
- 3.4 Express 浅析
- 4.1 开发环境
- 4.2 准备工作
- 4.3 配置文件
- 4.4 功能设计
- 4.5 页面设计
- 4.6 连接数据库
- 4.7 注册
- 4.8 登出与登录
- 4.9 文章
- 4.10 留言
- 4.11 404 页面
- 4.12 错误页面
- 4.13 日志
- 4.14 测试
- 4.15 部署