QBao-Web架构详解
qbao-web2.0
概述
前后端分离是这两年很热门的提法。
随着HTML5/CSS3广泛推广,前端框架不断进化,前端开发越来越专业化,自然想要摆脱后端的束缚,NodeJS的出现,也让前端工程师看到更多的可能。同时后端工程师也希望更多地专注于后台业务逻辑,而不是页面性能或是多浏览器适配。所以前后端分离,就是让“专业的人做专业的事”。
前后端分离不是一定要采用NodeJS做服务器,也不是一定要采用AngularJS,相反的,盲目引入新技术很容易让项目陷入困境,让开发人员不知所措,最好能采用一些温和的方式一步步来。
方案
采用Ajax模式,应用服务器只提供数据,由静态服务器或CDN承载HTML/CSS/JS文件,使用JS进行Ajax请求数据,动态生成最终页面。
这里对网站架构师提出了新的要求,需要定义完备的Restful接口。
不使用AngularJS重型前端框架,只使用前端工程师最熟悉的jQuery类库,同时引用HandlerBars模板语言辅助生成页面。
模块划分上使用NodeJS进行html暴力插入,可以考虑换成Jade模板语言。
只做多页面方案,尽量不做页面内跳转,避免引入前端路由。
详解
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- build:css ../components/components.min.css -->
<!-- components:css -->
<!-- endinject -->
<link rel="stylesheet" href="../common/styles/base.css">
<link rel="stylesheet" href="styles/index.css">
</head>
<body>
@@include('../components/site-top/site-top.html')
@@include('../components/site-header/site-header.html')
<div id="banner" class="container"></div>
<div class="main container clearfix">
<div id="auction-list" class="list"></div>
<div class="right-side">...</div>
</div>
...
@@include('../components/site-footer/site-footer.html')
<script id="banner-template" type="text/x-handlebars-template">
{ {#each list} }
<div>
<a href="{ {target} }">
<img src="{ {path} }" />
</a>
</div>
{ {/each} }
</script>
<script id="auction-list-template" type="text/x-handlebars-template">
<ul>
{ {#each list} }
<li class="item">
<p class="name">{ {product.name} }</p>
<section class="content">
<img class="picture" src="{ {picture} }" />
<div class="price-bar">
<div class="value">
<img src="images/rmb-icon-gray.png" />
<p class="number">{ {value} }</p>
<p class="note">市场价</p>
</div>
...
</section>
<footer>
<ul>
<li>
<p><span>{ {register} }</span>人</p>
<p class="note">报名人数</p>
</li>
...
</ul>
</footer>
</li>
{ {/each} }
</ul>
</script>
<script src="../bower_components/jquery/dist/jquery.min.js"></script>
<script src="../bower_components/handlebars/handlebars.min.js"></script>
<!-- components:js -->
<!-- endinject -->
<script src="scripts/index.js"></script>
</body>
</html>
这是一个很典型的页面,雷拍列表。
公用元素
由于没有后台服务器为我们组装页面,我在这里采用了自定义语法来引入公共控件,比如页头和页尾。
@@include('../components/site-top/site-top.html') @@include('../components/site-footer/site-footer.html')
这不是标准的html语法,所以这个页面也无法直接在浏览器中加载,需要使用NodeJS预编译成HTML发布。
这里的NodeJS作为编译工具出现,而不是服务器,理论上也可以使用NodeJS原配的Jade模板语言来完成,不过需要引入一个不那么像HTML语法的生成器,不是很爽。
有了这种设计,我们就不需要将公用元素在每个页面中重复,可以最大化地复用,也能方便地统一修改。
动态元素
动态元素由HandlerBars模板生成,比如这个Banner:
<script id="banner-template" type="text/x-handlebars-template"> { {#each list} } <div> <a href="{ {target} }"> <img src="{ {path} }" /> </a> </div> { {/each} } </script>
选用HandlerBars,一是因为它的语法很接近HTML,二是因为它非常快。
数据获取通过jQuery的ajax函数:
var loadBanner = function() { var source = $('#banner-template').html(); var template = Handlebars.compile(source); $.get('http://localhost:3000/banners/54c98efa2d2c4b7517e8c63f').done(function(resp) { var urlList = $.map(resp.pictures, function(picture) { picture.path = 'http://localhost:3000/uploads/' + picture.name; return picture; }); var html = template({ list: urlList }); $('#banner').append(html); }); };
目录结构
模块化地组织目录结构,可以方便地分人员开发和负责。
顶级目录
- app 存放页面模板
- dist 存放发布的最终页面,由自动化构建系统生成
- test 自动化测试用例
二级目录
- components 公共模块,页头、页尾、通用控件等
- common 公共资源,如网站的基础css
- 其他 各子模块,如雷拍、宝筹、任务中心等
三级目录
- index.tpl.html 子模块的首页模板,配有同名的js和css文件
- detail.tpl.html 子模块的其他页面模板,配有同名的js和css文件
自动化构建
使用Gulp搭建自动化构建环境,除了前述公用元素插入,自动化构建还支持:
- 静态代码检查
- JS和CSS文件的连接和压缩
- 图片的压缩优化
- 雪碧图和相应的CSS生成
浏览器自动重载等
细节在之后统一写一篇专题介绍。
改进点
- 需要考虑根据团队接受能力引入SASS,提升CSS架构能力
- 需要考虑把HandleBars模板预编译成JS再发布,可以大幅提升渲染速度
小结
优点
最容易落地的技术方案,避开AngularJS的学习曲线,只需要学习HandlerBars模板语言
简单粗暴的前后端分离,为之后的架构转型做准备,之后的进化就是前后端分别进行的了
缺点
- 首页加载速度不如服务器渲染,用户会看到数据加载过程
qbao-web2.1
随着项目越来越大,页面越来越多,web2.0的构建系统慢慢吃力起来,动辄要10分钟才可以构建一遍,而且要上传到构建服务器构建,成功后再从服务器上下载下来,构建周期太长,已经违反了架构的初衷。
针对这一情况,我们推出了web2.1框架,主要优化如下:
加速构建过程,在开发者本机就可以一键构建,自动生成Dist和CDN文件,并且在半分钟内构建完毕;
使用Reset.css替换Normalize.css;
控件支持按项目增删配置,而不是默认全部包含;
优化仍然是围绕着"自动化构建"开展的。