Day8:构建前端
在浏览器看到的画面,都是有浏览器解释才呈现出来的。实质它是一段HTML代码,外加JavaScript、CSS构成。如果把网页比作一个人,那么HTML便是他的骨架;javascript是肌肉;CSS是衣服。
在前面几天的工作中,我们初始化了数据库,完成了ORM框架和Web框架,并且跑通了一个简单的MVC,但是这个MVC中的view还是非常简单的,效果不能令人满意,为了使页面达到更好的效果,对于复杂的HTML前端页面来说,我们需要一套基础的CSS框架来完成页面布局和基本样式。
从零开始写CSS不如直接从一个已有的功能完善的CSS框架开始。有很多CSS框架可供选择。我们这次选择uikit这个强大的CSS框架。它具备完善的响应式布局,漂亮的UI,以及丰富的HTML组件,让我们能轻松设计出美观而简洁的页面。
由于教程中的资源文件以及过期,所以直接从廖老师github源代码中下载了相应文件。
所有的静态资源文件我们统一放到www/static目录下,和源文件一致就好。
由于前端页面肯定不止首页一个页面,每个页面都有相同的页眉和页脚。如果每个页面都是独立的HTML模板,那么我们在修改页眉和页脚的时候,就需要把每个模板都改一遍,这显然是没有效率的。
常见的模板引擎已经考虑到了页面上重复的HTML部分的复用问题。有的模板通过include把页面拆成三部分:
<html>
<% include file="inc_header.html" %>
<% include file="index_body.html" %>
<% include file="inc_footer.html" %>
</html>
这样,相同的部分inc_header.html和inc_footer.html就可以共享。
但是include方法不利于页面整体结构的维护。jinjia2的模板还有另一种“继承”方式,实现模板的复用更简单。
“继承”模板的方式是通过编写一个“父模板”,在父模板中定义一些可替换的block(块)。然后,编写多个“子模板”,每个子模板都可以只替换父模板定义的block。
这样,一旦定义好父模板的整体布局和CSS样式,编写子模板就会非常容易。
让我们通过uikit这个CSS框架来完成父模板base.html的编写:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
{% block meta %}<!-- block meta -->{% endblock %}
<title>{% block title %} ? {% endblock %} - Awesome Python Webapp</title>
<link rel="stylesheet" href="/static/css/uikit.min.css">
<link rel="stylesheet" href="/static/css/uikit.gradient.min.css">
<link rel="stylesheet" href="/static/css/awesome.css" />
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/md5.js"></script>
<script src="/static/js/uikit.min.js"></script>
<script src="/static/js/awesome.js"></script>
{% block beforehead %}<!-- before head -->{% endblock %}
</head>
<body>
<nav class="uk-navbar uk-navbar-attached uk-margin-bottom">
<div class="uk-container uk-container-center">
<a href="/" class="uk-navbar-brand">Awesome</a>
<ul class="uk-navbar-nav">
<li data-url="blogs"><a href="/"><i class="uk-icon-home"></i> 日志</a></li>
<li><a target="_blank" href="#"><i class="uk-icon-book"></i> 教程</a></li>
<li><a target="_blank" href="#"><i class="uk-icon-code"></i> 源码</a></li>
</ul>
<div class="uk-navbar-flip">
<ul class="uk-navbar-nav">
{% if user %}
<li class="uk-parent" data-uk-dropdown>
<a href="#0"><i class="uk-icon-user"></i> {{ }}</a>
<div class="uk-dropdown uk-dropdown-navbar">
<ul class="uk-nav uk-nav-navbar">
<li><a href="/signout"><i class="uk-icon-sign-out"></i> 登出</a></li>
</ul>
</div>
</li>
{% else %}
<li><a href="/signin"><i class="uk-icon-sign-in"></i> 登陆</a></li>
<li><a href="/register"><i class="uk-icon-edit"></i> 注册</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="uk-container uk-container-center">
<div class="uk-grid">
<!-- content -->
{% block content %}
{% endblock %}
<!-- // content -->
</div>
</div>
<div class="uk-margin-large-top" style="background-color:#eee; border-top:1px solid #ccc;">
<div class="uk-container uk-container-center uk-text-center">
<div class="uk-panel uk-margin-top uk-margin-bottom">
<p>
<a target="_blank" href="#" class="uk-icon-button uk-icon-weibo"></a>
<a target="_blank" href="#" class="uk-icon-button uk-icon-github"></a>
<a target="_blank" href="#" class="uk-icon-button uk-icon-linkedin-square"></a>
<a target="_blank" href="#" class="uk-icon-button uk-icon-twitter"></a>
</p>
<p>Powered by <a href="#">Awesome Python Webapp</a>. Copyright © 2014. [<a href="/manage/" target="_blank">Manage</a>]</p>
<p><a href="http://www.liaoxuefeng.com/" target="_blank">www.liaoxuefeng.com</a>. All rights reserved.</p>
<a target="_blank" href="#"><i class="uk-icon-html5" style="font-size:64px; color: #444;"></i></a>
</div>
</div>
</div>
</body>
</html>
我们把首页改造一下,从_base_.html继承一个blogs.html:
{% extends '__base__.html' %}
{% block title %}日志{% endblock %}
{% block content %}
<div class="uk-width-medium-3-4">
{% for blog in blogs %}
<article class="uk-article">
<h2><a href="/blog/{{ blog.id }}">{{ }}</a></h2>
<p class="uk-article-meta">发表于{{ blog.created_at}}</p>
<p>{{ blog.summary }}</p>
<p><a href="/blog/{{ blog.id }}">继续阅读 <i class="uk-icon-angle-double-right"></i></a></p>
</article>
<hr class="uk-article-divider">
{% endfor %}
</div>
<div class="uk-width-medium-1-4">
<div class="uk-panel uk-panel-header">
<h3 class="uk-panel-title">友情链接</h3>
<ul class="uk-list uk-list-line">
<li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="#">编程</a></li>
<li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="#">读书</a></li>
<li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="#">Python教程</a></li>
<li><i class="uk-icon-thumbs-o-up"></i> <a target="_blank" href="#">Git教程</a></li>
</ul>
</div>
</div>
{% endblock %}
最后再创建MVC并运行,将昨天的URL处理函数更新如下:
@get('/')
def index(request):
summary = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
blogs = [
Blog(id='1', name='Test Blog', summary=summary, created_at=time.time()-120),
Blog(id='2', name='Something New', summary=summary, created_at=time.time()-3600),
Blog(id='3', name='Learn Swift', summary=summary, created_at=time.time()-7200)
]
return {
'__template__': 'blogs.html',
'blogs': blogs
}
Blog的创建日期显示的是一个浮点数,因为它是由这段模板渲染出来的:
<p class="uk-article-meta">发表于{{ blog.created_at }}</p>
解决方法是通过jinja2的filter(过滤器),把一个浮点数转换成日期字符串:
<p class="uk-article-meta">发表于{{ blog.created_at|datetime }}</p>
还记得day5中初始化jinja2时候我们有编写一个datetime_filter() 的过滤器,它在这里起到的作用就是将浮点数转换成正确的日期时间数。
现在再通过浏览器访问 localhost:9000/ 就可以看到很高大上的博客界面啦。