浅谈HTML5单页面架构(三)—— 回归本真:自定义路由 + requirejs + zepto + underscore
本文转载自:http://www.cnblogs.com/kenkofox/p/4650310.html
不过,这一篇,我想进一步探讨一下这两个框架的优缺点,另外,再进一步,抛开这两个框架,回到本真,自己搞个简单的路由一样可以实现单页面。
这个对于刚做前端开发的新同学来说就最好不过了,如果一来到岗位就一大堆angular、backbone、requirejs,看资料都看一两周。其实大家最熟悉的东西还是那个美元$,用美元能解决的问题,就不要麻烦到angular、backbone大爷了。
事先说明,由于我的业务范围窄,不一定能把angular和backbone的功能都用一遍,所以以下的分析可能以偏概全,欢迎大家讨论。
angular优点:
- 强大的数据双向绑定
- View界面层组件化
- 内置的强大服务(例如表单校验)
- 路由简单
angular缺点:
- 引入的js较大,对移动端来说有点吃不消
- 语法复杂,学习成本高
backbone优点:
- 引入的js较小
- 清晰MVC分层
- Model层事件机制
- 路由简单而且便于扩展
backbone缺点:
- MVC有点死板,有时候觉得累赘
- 没有双向绑定,界面修改只能靠自己
- view切换时,没有足够便捷的事件通知(要自己监听route)
其实,这两个框架都非常优秀,但是,在实际业务中,不一定百试百灵,因为有一些移动端的单页面web,业务就很简单,只是路由分别切换到几个子模块,每个子模块基本都是拉一次数据,展示给用户,很少用户交互从而修改数据,改变视图的功能。
对于这种情况,使用angular未免有点杀鸡用牛刀的感觉,而backbone虽然小巧了不少,但是模型的功能也是浪费的。
所以,在这里,我想探讨一下,能否抛开这两个框架,只索取我们基本所需,建立一个更简单的架构呢?
经验看来,一些类库是必不可少的:
- requirejs:模块划分
- zepto:移动端的jquery
- underscore:便捷的基础方法,包括模版template、each、map等等
- 路由库:这里先使用director.js,然而这玩意并没有backbone和angular的路由好用,文章最后再来探讨这个问题
自己做一套最简单的架构,思想非常简单:
- 启动程序
- 监听路由
- 路由变化,映射到对应的处理逻辑,加载对应的模块
- 模块加载完成,修改dom,也就是视图
- 页面跳转时,移除上一个模块,加载下一个模块,也就是回到第3点
简单的思路,让架构非常简洁明了,新团队成员来到能够轻松上手,而angular和backbone的架构,少说得2、3天才能融入一个已有项目中去。
接下来,我们具体看看怎么做。
第一步,还是index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Underscore & Director & Requirejs</title>
</head> <body>
<div id="container"></div>
<script data-baseurl="./" data-main="main.js" src="libs/require.js" id="main"></script>
</body>
</html>

这个跟前两篇没什么差别。requirejs引入main.js作为程序入口
第二步,main.js配置requirejs的依赖关系,并启动webapp

(function (win) {
//配置baseUrl
var baseUrl = document.getElementById('main').getAttribute('data-baseurl'); /*
* 文件依赖
*/
var config = {
baseUrl: baseUrl, //依赖相对路径
paths: { //如果某个前缀的依赖不是按照baseUrl拼接这么简单,就需要在这里指出
director: 'libs/director',
zepto: 'libs/zepto.min',
underscore: 'libs/underscore',
text: 'libs/text' //用于requirejs导入html类型的依赖
},
shim: { //引入没有使用requirejs模块写法的类库。
underscore: {
exports: '_'
},
zepto: {
exports: '$'
},
director: {
exports: 'Router'
}
}
}; require.config(config);
require(['zepto', 'router', 'underscore'], function($, router, _){
win.appView = $('#container'); //用于各个模块控制视图变化
win.$ = $; //暴露必要的全局变量,没必要拘泥于requirejs的强制模块化
win._ = _;
router.init(); //开始监控url变化
}); })(window);

director.js没有AMD写法,还是按照shim的方式引入。另外,由于$和_的使用率太高,所以这里直接公开为全局变量。
除此之外,还加了appView变量,目的是方便各个子模块修改界面。
第三步,router.js配置路由
这里使用的路由类库是director(https://github.com/flatiron/director),相对精简的路由,但其实对于我们这个程序来说,貌似还不够精简。先凑合着吧。
director官网给出的示例也相当简单,就是“路径”对应“函数”,非常清晰而且实用的方式。

var author = function () { console.log("author"); };
var books = function () { console.log("books"); };
var viewBook = function (bookId) {
console.log("viewBook: bookId is populated: " + bookId);
}; var routes = {
'/author': author,
'/books': [books, function() {
console.log("An inline route handler.");
}],
'/books/view/:bookId': viewBook
}; var router = Router(routes); router.init();

来看看我们自己的版本:

define(['director', 'underscore'], function (Router, _) { //先设置一个路由信息表,可以由html直出,纯字符串配置
var routes = {
'module1': 'module1/controller1.js',
'module2/:name': 'module2/controller2.js' //director内置了普通必选参数的写法,这种路由,必须用路径“#module2/kenko”才能匹配,无法缺省
// 'module2/?([^\/]*)/?([^\/]*)': 'module2/controller2.js' //可缺省参数的写法,其实就是正则表达式,括号内部分会被抽取出来变成参数值。backbone做得比较好,把这个语法简化了
// “ /?([^\/]*) ” 这样的一段表示一个可选参数,接受非斜杠/的任意字符
}; var currentController = null; //用于把字符串转化为一个函数,而这个也是路由的处理核心
var routeHandler = function (config) {
return function () {
var url = config;
var params = arguments;
require([url], function (controller) {
if(currentController && currentController !== controller){
currentController.onRouteChange && currentController.onRouteChange();
}
currentController = controller;
controller.apply(null, params);
});
}
}; for (var key in routes) {
routes[key] = routeHandler(routes[key]);
} return Router(routes);
});

这里把director的路由配置修改了一下,原来只能接受<String, Function>这样的key value对,但参考之前backbone篇,更好方式应该是让路由表尽量只有字符串配置,不要写逻辑(函数)。
所以,上述代码中,多了一个routeHandler,目的就是建立闭包,把string(配置)转换为一个闭包函数。
结果,运行效果就是,遇到一个路由,就根据配置加载对应的子模块代码。后续实际执行什么,由子模块自己决定。这样main/router就能彻底跟子模块解耦。
第四步,建立一个模块
tpl.html
<div>
Here is module 1. My name: <%=name %><br>
<a href="#module2/fromModule1">turn to module 2</a>
</div>
controller1.js

define(['text!module1/tpl.html'], function (tpl) { var controller = function () {
appView.html(_.template(tpl, {name: 'kenko'}));
};
return controller;
});

我觉得能实现业务逻辑的前提下,越简单的架构就越好,便于传承和维护。
controller就是这个子模块要做的逻辑,appView是整个视图根节点,想怎么玩就怎么玩,这对于不熟悉angular、backbone的同学最爽不过了。
这里重点是利用了requirejs做模块化和依赖加载,并用了underscore的模版库template。
第五步,再做一个模块,加上一些销毁接口
tpl.html
<div>
Here is module 2. My name: <%=name %><br>
<button>click me!</button>
<a href="#module1">turn to module 1</a>
</div>
controller2.js

define(['text!module2/tpl.html'], function (tpl) { var controller = function (name) {
appView.html(_.template(tpl, {name: name?name:'vivi'})); $('button').on('click', function clickHandler() {
alert('hello');
}); controller.onRouteChange = function () {
console.log('change'); //可以做一些销毁工作,例如取消事件绑定
$('button').off('click'); //解除所有click事件监听
};
}; return controller;
});

至此,整个简单的框架就完成了。
大道至简,我非常喜欢这样简单的架构。希望对新手朋友有所帮助。
最后,关于director的路由,要吐槽一下,这个并没有backbone那些这么好用,它没有内置的缺省参数写法,需要自己理解正则表达式,写复杂的([?*。参照上边router.js的代码。
路由匹配的本质,其实是正则表达式的exec匹配和提取参数。我后续会再整一个简单好用的路由,参考backbone的模式,猛击这里:http://www.cnblogs.com/kenkofox/p/4650824.html
本文代码:https://github.com/kenkozheng/HTML5_research/tree/master/UnderscoreRequireJS
浅谈HTML5单页面架构(三)—— 回归本真:自定义路由 + requirejs + zepto + underscore的更多相关文章
- 浅谈HTML5单页面架构(二)——backbone + requirejs + zepto + underscore
本文转载自:http://www.cnblogs.com/kenkofox/p/4648472.html 上一篇<浅谈HTML5单页面架构(一)--requirejs + angular + a ...
- AngularJS进阶(二十五)requirejs + angular + angular-route 浅谈HTML5单页面架构
requirejs + angular + angular-route 浅谈HTML5单页面架构 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又 ...
- 浅谈HTML5单页面架构(一)——requirejs + angular + angular-route
心血来潮,打算结合实际开发的经验,浅谈一下HTML5单页面App或网页的架构. 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又要数单页面架构体验 ...
- H5单页面架构:自定义路由 + requirejs + zepto + underscore
angular优点: 强大的数据双向绑定 View界面层组件化 内置的强大服务(例如表单校验) 路由简单 angular缺点: 引入的js较大,对移动端来说有点吃不消 语法复杂,学习成本高 backb ...
- 浅谈vue单页面seo问题
最近做项目的时候,被要求做seo,由于项目已经开发完毕,且只需首页做seo,所以考虑再三,决定用prerender-spa-plugin结合vue-meta-info来实现首页的seo.如果你的页面是 ...
- 【ASP.NET MVC系列】浅谈表单和HTML辅助方法
[01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作篇)(下) [04]浅谈ASP. ...
- 【ASP.NET MVC系列】浅谈ASP.NET 页面之间传值的几种方式
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
- 浅谈大型web系统架构
动态应用,是相对于网站静态内容而言,是指以c/c++.php.Java.perl..net等服务器端语言开发的网络应用软件,比如论坛.网络相册.交友.BLOG等常见应用.动态应用系统通常与数据库系统. ...
- 浅谈html5网页内嵌视频
更好的阅读体验:浅谈html5网页内嵌视频 如今在这个特殊的时代下:flash将死未死,微软和IE的历史问题,html5标准未定,苹果和谷歌的闭源和开源之争,移动互联网的大势所趋,浏览器各自为战... ...
随机推荐
- MongoDB 创建 Database 和 Collection
在开始使用MongoDB(Version:3.2.9)之前,必须首先在MongoDB中创建 Database 和 Collection.Database是相互独立的,每个Database都有自己的Co ...
- SQL 数据库管理---公司培训
一.实例 一个SQL的服务引擎就是一个SQL实例,每装一次SQL就会产生一次实例. 实例分为命名实例和默认实例,一台Windows服务器可以有多个SQL实例,但是只能有一个默认实例. 不同的实例之间相 ...
- 前端学PHP之变量
× 目录 [1]变量定义 [2]关键字 [3]变量赋值[4]可变变量[5]变量函数 前面的话 变量是用于临时存储值的容器.这些值可以是数字.文本,或者复杂得多的排列组合.变量在任何编程语言中都居于核心 ...
- 爆一个VS2015 Update1更新带来的编译BUG【已有解决方案】
一个编译的BUG,在原生的VS2015中没有问题,但更新至VS2015 Update1之后就有了,大概是Update1用了新的编译器,害得我好苦.经测试,VS2013 Update5中也存在这个问题, ...
- Android总结之json解析(FastJson Gson 对比)
前言: 最近为了统一项目中使用的框架,发现项目中用到了两种json解析框架,他们就是当今非常主流的json解析框架:google的Gson 和阿里巴巴的FastJson,为了废除其中一个所以来个性能和 ...
- 可惜Java中没有yield return
项目中一个消息推送需求,推送的用户数几百万,用户清单很简单就是一个txt文件,是由hadoop计算出来的.格式大概如下: uid caller 123456 12345678901 789101 12 ...
- Android随笔之——跨进程通信(一) Activity篇
在Android应用开发中,我们会碰到跨进程通信的情况,例如:你用QQ通讯录打电话的时候会调用系统的拨号应用.某些新闻客户端可以将新闻分享到QQ.微信等应用,这些都是跨进程通信的情况.简而言之,就是一 ...
- geotrellis使用(三)geotrellis数据处理过程分析
之前简单介绍了geotrellis的工作过程以及一个简单的demo,最近在此demo的基础上实现了SRTM DEM数据的实时分析以及高程实时处理,下面我就以我实现的上述功能为例,简单介绍一下geotr ...
- 如何用easyui+JAVA 实现动态拼凑datagrid表格
先给大家看一看效果,最近一段时间都在研究这个东西. 如果我把日期间隔选宽呢?比如5月日到5月5日?下面给大家看看效果,不用担心哦 看到了吧,哈哈,这个日期都是动态生成的,下面就来跟大家分享一下这个的实 ...
- 在IIS下部署Thinkphp项目,验证码不能显示的解决办法
由于公司租用的是虚拟空间,而且用的是IIS服务器,所以部署PHP的时候就出现很多问题:比如昨天就碰到这个问题:在IIS下部署Thinkphp项目,验证码不能显示 这是生成验证码的方法: // 制作专门 ...