Popush代码阅读报告——View部分
聂中天2014/11/06
在真正集体开发之前,先要弄清楚如何编写。Popush集成了许多框架和设计模式,需要参照其官方文档和Popush的具体实现对比分析,才能合理高效地扩展Popush的功能。
这一篇博文就Popush的主要前端实现机制作一个整理总结,并对部分细节测试分析。针对的文件为Popush目录下Static文件夹里的文件。
前端使用了Backbonejs的框架,MVC体系结构。Model和Collection描述file和user数据,这里就不展开了。View通过js/view文件夹下的javascript描述页面内的响应逻辑。最后router.js反应页面跳转。另外浏览器端用Socket.io实现与服务器的信息交流。
大体上这样。然后是细节。
1. Router.js
因为Backbone采用的是单页面式的网页形式,用一个网页中元素的出现、消失等动作来表现页面跳转,Router.js就是处理跳转逻辑。相当于Controller的角色。
var app = app || {};
(function() {
var Page = app.Page = function(opts) {
this.shown = false;
_.extend(this, opts);
return this;
};
Page.prototype.show = function() { this.el.show(); };
Page.prototype.hide = function() { this.el.hide(); };
Page.prototype.fadeIn = function() { this.el.fadeIn('fast'); };
Page.prototype.fadeOut = function() { this.el.fadeOut('fast'); };
首先获取全局变量app。下面的函数定义了Page这一对象,称之页面元素更合适,是本文件的基本数据类型。它的shown默认值是false,定义其显示、消失、淡入、淡出方法的默认函数,是该对象的el元素执行jQuery自带的操作。
var PageRouter = Backbone.Router.extend({
routes: {
'login': function() { this.analy('login'); },
'register': function() { this.analy('register'); },
'index/*filepath': function(arg1) { this.analy('index', arg1); },
'/*filepath': function(arg1) { this.analy('index', arg1); },
'edit/': function(arg1) { this.analy('edit'); },
},
定义了“五个页面”,之所以打引号是因为这些页面都只是在单页面URL加上后缀而已。后缀名是这个对象的方法名,而第三四个*filepath可匹配参数。当网页前端(比如a标签)链接到“#index/file1/file2”的时候,会由router匹配到第三个路由项,*filepath可对应得到URL中的“fil1/file2”字符串,作为后面参数arg1,去执行响应函数,也就是跳转操作了。
第五个参数arg1是多余的。统一用下面定义的analy函数响应。
pages: {
login: new Page({
el: '#login',
depend: ['_head1', '_footer', '_ads'],
logined: 0,
force: true,
show: function() {
this.el.show();
app.views.login.show();
},
hide: function() {
this.el.find('#login-padding').slideDown();
this.el.hide();
},
}),
register: new Page({
el: '#register',
depend: ['_head1', '_footer', '_ads'],
接下来的实现感觉有点绕,意思是先在pages属性里定义好了所有共四个页面。包括其需要保存的信息(如是否登陆)和显示隐藏的处理。force属性没看懂。
analy函数很长。一上来先用routeLock锁上,没有执行完此函数不对页面跳转再做反应(虽然URL还是会变);一些判断检查;recurse函数递归把该页面显示需要加载的页面元素、子页面加入数组arr里,随后逐一显示出来。(里面有个坑爹的变量j,没有说明,似乎是用来描述当前页面状态的)。
最后实例化PageRouter对象到app.router。 在某个其他地方启动了Backbone.history。至此,路由完成。
2. View 文件
这些文件统一以-view结尾,里面也是一个立即函数,即直接执行的程序,使用Backbone.View.extend定义了app的视图成员。
var app = app || {};
(function () {
app.AccountView = Backbone.View.extend({
el: '#nav-head',
events: {
'click #btn_changepassword': 'changepasswordopen',
'click #btn_changeavatar': 'changeavataropen',
'click .go-logout': 'logout',
},
initialize: function () {
var that = this;
首先获取全局变量app,把所有页面都定义为它的属性。接下来代码全部包裹在一个即时函数中,‘use strict’表示是JavaScript的严格模式,这样在这一段函数中的所有代码会受到更严格的限制,否则抛出异常。部分view文件有,说明上一代编写者也没有很在乎。
app.LoginView = Backbone.View.extend({
el: '#login',
events: {
'keypress #login-inputName': 'loginOnEnter',
'keypress #login-inputPassword': 'loginOnEnter',
'click #login-submit': 'login'
},
login: function () {
var name = $('#login-inputName').val(),
in_pw = $('#login-inputPassword');
var pass = in_pw.val();
接下来,利用Backbone的View定义视图对象。这里app.LoginView就是一个前端的页面,已经在Router里面添加过链接了。再用View.extend函数定义动作。
该函数的参数是一个对象,其中el属性将index.html中的DOM元素和这个视图绑定,events属性定义了前端的事件响应,比如当id为’login-inputName’的输入框接收到按键输入时,会触发loginOnEnter的事件。View.extend的参数的方法就是响应事件的处理函数,比如login方法会响应“点击登陆”。
有的地方没有el,而是如下形式:
app.SharerView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#sharer-template').html()),
events: {
'click a': 'select'
},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'remove', this.remove);
this.listenTo(this.model, 'destroy', this.remove);
},
它会找到index中下面的模版代码,利用模版加上信息,动态创建Dom列表元素(li)。填充这个模版里头像(avatar)、姓名(name)等元素时需要用到model。
<script type="text/template" id="sharer-template">
<a href="#">
<img src="<%= avatar %>" width="32" height="32" class="sharer-avatar userlistimg user-<%= name %>" />
<%=name%>
</a>
</script>
这类视图的初始化中用了事件监听ListenTo,可以随时监听model,相当于数据部分的变动。
3. Index.html
头尾是脚本、样式等等不谈,中间定义了整个网站所有的Dom节点。
说得更具体一点,包括了所有后台需要操作的静态标签,按钮,滑动框,交互元素。之后是需要弹出的区块,再后面是模版(比如文件列表)。
更具体不能了。就算我写了你也没兴趣读吧。
小结
阿西,index太长了,view文件太多了,注释太少了,又写得匆忙,疏漏颇多,请看官见谅。
APEC愉快!