【再探backbone 03】博客园单页应用实例(提供源码)

前言

之前尝试性的读了下backbone的源码,对继承、事件、view一块还比较熟悉,看下去比较顺畅,但是在model collection方面就很不顺利

究其原因还是没有使用过,不够熟悉,以码读码,脱离了业务逻辑的代码毫无意义,所以今天我们先来做一个例子吧,然后再根据例子学习

今天来一段官网以外的代码,本来这里想抄一个代码来着,但是这样的话好像意义就不大了,于是自己重新写一个例子吧

注意这个例子只是简单例子,各位不要当真,而且代码是今天下午写的,有BUG不要骂我,然后放到IIS环境下才有数据

下载源码:02backbone.zip

单页应用实例

以博客园为例,我们一起做一个单页,提供list和detail两个页面,并且为list提供分页和简单筛选功能来巩固我们的backbone学习

文件结构

首先为了简单起见,我们这里目录暂时如下:

【再探backbone 03】博客园单页应用实例(提供源码)

其中css样式是上次做单页应用研究剩下的,这次直接拿来用吧

数据源

然后,我们来简单看看我们博客园的数据源返回的数据:

【再探backbone 03】博客园单页应用实例(提供源码)

很遗憾博客园暂时只提供了xml的返回格式,其中我自己做了json的解析,其实就算我自己不做解析,也可以通过model的parse做解析

但是我没有那么蛋疼,这里还是使用后台方法解析吧

Model/Collection

有了数据源,我们就需要对其创建数据模型了,这里可以与他统一反正数据model会自己对应

我们知道Model可以设置URL,自己从服务器获取数据(只不过必须是rest请求),我们这里来试试

 1 //博客模型

 2 var PostModel = Backbone.Model.extend({

 3 

 4 });

 5 

 6 //模型集合

 7 var PostList = Backbone.Collection.extend({

 8   model: PostModel

 9 });

10 var curpage = 1;

11 var pageSize = 10;

12 var list = new PostList();

13 list.url = 'Handler.ashx?url=http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize;

14 list.fetch();

我们这里事实上只需要获取数据即可,如此变会发起一个请求,数据返回与上述截图一致

有了数据我们就需要根据数据对model进行填充,一次实例化model加入collection

我们这里使用parse对返回数据进行处理,于是他自己就将model填充到model里面去了

parse: function (data) {

  // 'data' contains the raw JSON object

  return (data && data.feed && data.feed.entry) || {}

}



list.fetch({

  success: function () {

    var s = '';

  }

});

【再探backbone 03】博客园单页应用实例(提供源码)

需要注意一点是这里是异步的,所以不要傻傻的马上想在后面操作数据

视图-View

现在数据模型与集合都有了,我们需要一个承载他的页面,于是我们先随便搞下将数据显示出来:

① 定义模板

 1 <script type="text/template" id="item-template">

 2   <li class="arr_r orderItem" data-id="<%=id %>">

 3   <article class="blog_item">

 4     <h3>

 5       <a href="<%=link.href %>" target="_blank">

 6         <%=title.value || '无题' %></a>

 7     </h3>

 8     <div class="author pro_list_rank">

 9       <%if(author.avatar){ %>

10       <a href="<%=author.uri %>" target="_blank">

11         <img src="<%=author.avatar %>">

12       </a>

13       <%} %>

14       <%=summary.value %>

15     </div>

16     <div class="item_footer">

17       <a href="<%=author.uri %>" class="lightblue">Scut</a>

18       <%=published %>

19       <a href="<%=link.href %>" title="2013-08-21 15:21" class="gray">评论(<%=comments %>)</a>

20       <a href="<%=link.href %>" class="gray">阅读(<%=views %>)</a> <span class="price1">推荐(<%=diggs %>)</span></div>

21   </article>

22 </li>

23 </script>

这是我们之前使用过的数据模板,这里直接搞过来使用了

② 定义view

并在集合数据加载结束调用render方法

 1 var View = Backbone.View.extend({

 2   template: _.template($('#item-template').html()),

 3   initialize: function () {

 4     var scope = this;

 5     var curpage = 1;

 6     var pageSize = 10;

 7     this.list = new PostList();

 8     this.list.url = 'Handler.ashx?url=http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize;

 9     this.list.fetch({

10       success: function () {

11         scope.render();

12       }

13     });

14     this.wrapper = $('#lstbox');

15   },

16   render: function () {

17     var models = this.list.models;

18     var html = '';

19     for (var i = 0, len = models.length; i < len; i++) {

20       html += this.template(models[i].toJSON());

21 

22     }

23     this.wrapper.html(html);

24     var s = '';

25   }

26 });

③ 实例化view

var view = new View();

于是我们的view就出来了:

【再探backbone 03】博客园单页应用实例(提供源码)

附上完整html代码:

【再探backbone 03】博客园单页应用实例(提供源码)
  1 <!DOCTYPE html>

  2 <html xmlns="http://www.w3.org/1999/xhtml">

  3 <head>

  4   <meta charset="utf-8" />

  5   <title></title>

  6   <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />

  7   <link rel="Stylesheet" type="text/css" href="res/style/main2.css" />

  8   <link rel="Stylesheet" type="text/css" href="res/style/tuan.css" />

  9   <style> .pro_list_rank { margin: 5px 0; padding-right: 22px; }

 10     .figcaption span { text-align: center; }

 11     .blog_item {}

 12     .blog_item img { width: 48px; height; 48px; margin: 4px; padding: 1px; float: left; border: 1px solid #CCC;  }

 13     

 14     .blog_item .item_footer { color: #757575; font-size: 0.86em; }

 15     a { color:  #005A94; }

 16     .tab_hotel { border-left: 1px solid #2B97E2; }

 17     .cont_wrap .content { background-color: White; padding: 5px 10px; }

 18     img { max-width: 98%; }</style>

 19 </head>

 20 <body>

 21   <div class="main-frame">

 22     <div class="main-viewport">

 23       <header>

 24         <b class="icon_home i_bef" id="js_home"></b>

 25         <h1>

 26          博客园</h1>

 27         <i id="js_return" class="returnico"></i>

 28       </header>

 29       <section class="cont_wrap" >

 30         <ul class="pro_list" id="lstbox">

 31         </ul>

 32       </section>

 33       <ul class="tab_search fix_bottom">

 34         <li class="tabcrt">时间</li>

 35         <li class="tab_hotel ">推荐</li>

 36         <li class="tab_hotel ">阅读</li>

 37         <li class="tab_hotel ">评论</li>

 38       </ul>

 39     </div>

 40   </div>

 41 <script type="text/template" id="item-template">

 42   <li class="arr_r orderItem" data-id="<%=id %>">

 43   <article class="blog_item">

 44     <h3>

 45       <a href="<%=link.href %>" target="_blank">

 46         <%=title.value || '无题' %></a>

 47     </h3>

 48     <div class="author pro_list_rank">

 49       <%if(author.avatar){ %>

 50       <a href="<%=author.uri %>" target="_blank">

 51         <img src="<%=author.avatar %>">

 52       </a>

 53       <%} %>

 54       <%=summary.value %>

 55     </div>

 56     <div class="item_footer">

 57       <a href="<%=author.uri %>" class="lightblue">Scut</a>

 58       <%=published %>

 59       <a href="<%=link.href %>" title="2013-08-21 15:21" class="gray">评论(<%=comments %>)</a>

 60       <a href="<%=link.href %>" class="gray">阅读(<%=views %>)</a> <span class="price1">推荐(<%=diggs %>)</span></div>

 61   </article>

 62 </li>

 63 </script>

 64   <script src="libs/jquery.js" type="text/javascript"></script>

 65   <script src="libs/underscore.js" type="text/javascript"></script>

 66   <script src="libs/backbone.js" type="text/javascript"></script>

 67   <script type="text/javascript" src="libs/backbone.localStorage.js"></script>

 68   <script type="text/javascript">

 69     //博客模型

 70     var PostModel = Backbone.Model.extend({

 71 

 72     });

 73 

 74     //模型集合

 75     var PostList = Backbone.Collection.extend({

 76       model: PostModel,

 77       parse: function (data) {

 78         // 'data' contains the raw JSON object

 79         return (data && data.feed && data.feed.entry) || {}

 80       }

 81     });

 82 

 83 var View = Backbone.View.extend({

 84   template: _.template($('#item-template').html()),

 85   initialize: function () {

 86     var scope = this;

 87     var curpage = 1;

 88     var pageSize = 10;

 89     this.list = new PostList();

 90     this.list.url = 'Handler.ashx?url=http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize;

 91     this.list.fetch({

 92       success: function () {

 93         scope.render();

 94       }

 95     });

 96     this.wrapper = $('#lstbox');

 97   },

 98   render: function () {

 99     var models = this.list.models;

100     var html = '';

101     for (var i = 0, len = models.length; i < len; i++) {

102       html += this.template(models[i].toJSON());

103 

104     }

105     this.wrapper.html(html);

106     var s = '';

107   }

108 });

109 

110     var view = new View();

111     var s = '';

112 

113   </script>

114 </body>

115 </html>
完整Html代码

到现在为止,我们只是简单将视图展示了出来,现在我们来多用一点集合与模型的操作,深入之前的学习

操作集合/模型

这里又有个比较遗憾的地方就是,我们并不需要写数据到服务器,我这里也不准备调用写的接口,所以model又被边缘化了......

集合排序

我们这里对下面的几个做一个排序处理,比如按时间排序,按推荐数排序等,这里就要用的集合里面的操作了

然后我们来看看我们如何只改变集合的数据,而让dom自己发生变化

有一点需要注意的是,我们现在还不是完整的单页,后面引入路由时候慢慢改进程序结构

首先我们需要为下面几个可爱的按钮绑定点击事件:

1   <ul class="tab_search fix_bottom" id="sort">

2     <li class="tabcrt" attr="time">时间</li>

3     <li class="tab_hotel" attr="recommend">推荐</li>

4     <li class="tab_hotel" attr="read">阅读</li>

5     <li class="tab_hotel" attr="comment">评论</li>

6   </ul>
1 events: {

2   'click #sort': function (e) {

3     var el = $(e.target);

4     var sort = el.attr('attr');

5 

6     var s = '';

7   }

8 },

【再探backbone 03】博客园单页应用实例(提供源码)

events: {

  'click #sort': function (e) {

    var el = $(e.target);

    var type = el.attr('attr');

    this.list.setComparator(type);

    this.list.sort();

  }

},



//list新增方法

setComparator: function (type) {

  this.comparator = function (item) {

    return Math.max(item.attributes[type]);

  }

}

这个时候在初始化时候再注册一个事件

this.listenTo(this.list, 'all', this.render);

于是就能点击不同的标签按不同的倒叙排列,这里注意一点是每次改变都会触发list的all事件(其实应该是reset),完了触发view的render重新渲染了下数据

【再探backbone 03】博客园单页应用实例(提供源码)
  1 <!DOCTYPE html>

  2 <html xmlns="http://www.w3.org/1999/xhtml">

  3 <head>

  4   <meta charset="utf-8" />

  5   <title></title>

  6   <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />

  7   <link rel="Stylesheet" type="text/css" href="res/style/main2.css" />

  8   <link rel="Stylesheet" type="text/css" href="res/style/tuan.css" />

  9   <style> .pro_list_rank { margin: 5px 0; padding-right: 22px; }

 10     .figcaption span { text-align: center; }

 11     .blog_item {}

 12     .blog_item img { width: 48px; height; 48px; margin: 4px; padding: 1px; float: left; border: 1px solid #CCC;  }

 13     

 14     .blog_item .item_footer { color: #757575; font-size: 0.86em; }

 15     a { color:  #005A94; }

 16     .tab_hotel { border-left: 1px solid #2B97E2; }

 17     .cont_wrap .content { background-color: White; padding: 5px 10px; }

 18     img { max-width: 98%; }</style>

 19 </head>

 20 <body>

 21   <div class="main-frame">

 22     <div class="main-viewport" id="main-viewport">

 23     </div>

 24   </div>

 25   <script type="text/template" id="index-template">

 26   <header>

 27     <b class="icon_home i_bef" id="js_home"></b>

 28     <h1>

 29       博客园</h1>

 30     <i id="js_return" class="returnico"></i>

 31   </header>

 32   <section class="cont_wrap">

 33     <ul class="pro_list" id="lstbox">

 34     </ul>

 35   </section>

 36   <ul class="tab_search fix_bottom" id="sort">

 37     <li class="tabcrt" attr="updated">时间</li>

 38     <li class="tab_hotel" attr="diggs">推荐</li>

 39     <li class="tab_hotel" attr="views">阅读</li>

 40     <li class="tab_hotel" attr="comments">评论</li>

 41   </ul>

 42   </script>

 43   <script type="text/template" id="item-template">

 44   <li class="arr_r orderItem" data-id="<%=id %>">

 45   <article class="blog_item">

 46     <h3>

 47       <a href="<%=link.href %>" target="_blank">

 48         <%=title.value || '无题' %></a>

 49     </h3>

 50     <div class="author pro_list_rank">

 51       <%if(author.avatar){ %>

 52       <a href="<%=author.uri %>" target="_blank">

 53         <img src="<%=author.avatar %>">

 54       </a>

 55       <%} %>

 56       <%=summary.value %>

 57     </div>

 58     <div class="item_footer">

 59       <a href="<%=author.uri %>" class="lightblue">Scut</a>

 60       <%=published %>

 61       <a href="<%=link.href %>" title="2013-08-21 15:21" class="gray">评论(<%=comments %>)</a>

 62       <a href="<%=link.href %>" class="gray">阅读(<%=views %>)</a> <span class="price1">推荐(<%=diggs %>)</span></div>

 63   </article>

 64 </li>

 65 </script>

 66   <script src="libs/jquery.js" type="text/javascript"></script>

 67   <script src="libs/underscore.js" type="text/javascript"></script>

 68   <script src="libs/backbone.js" type="text/javascript"></script>

 69   <script type="text/javascript" src="libs/backbone.localStorage.js"></script>

 70   <script type="text/javascript">

 71     //博客模型

 72     var PostModel = Backbone.Model.extend({

 73 

 74     });

 75 

 76     //模型集合

 77     var PostList = Backbone.Collection.extend({

 78       model: PostModel,

 79       parse: function (data) {

 80         // 'data' contains the raw JSON object

 81         return (data && data.feed && data.feed.entry) || {}

 82       },

 83       setComparator: function (type) {

 84         this.comparator = function (item) {

 85           return Math.max(item.attributes[type]);

 86         }

 87       }

 88     });

 89 

 90     var View = Backbone.View.extend({

 91       el: $('#main-viewport'),

 92       template: _.template($('#index-template').html()),

 93       itemTmpt: _.template($('#item-template').html()),

 94 

 95       events: {

 96         'click #sort': function (e) {

 97           var el = $(e.target);

 98           var type = el.attr('attr');

 99           this.list.setComparator(type);

100           this.list.sort();

101         }

102       },

103       initialize: function () {

104         //先生成框架html

105         this.$el.html(this.template());

106 

107         var scope = this;

108         var curpage = 1;

109         var pageSize = 10;

110         this.list = new PostList();

111         this.list.url = 'Handler.ashx?url=http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize;

112         this.list.fetch({

113           success: function () {

114             scope.render();

115           }

116         });

117         this.wrapper = $('#lstbox');

118 

119         this.listenTo(this.list, 'all', this.render);

120 

121       },

122       render: function () {

123 

124         var models = this.list.models;

125         var html = '';

126         for (var i = 0, len = models.length; i < len; i++) {

127           html += this.itemTmpt(models[i].toJSON());

128         }

129         this.wrapper.html(html);

130         var s = '';

131       }

132     });

133 

134     var view = new View();

135     var s = '';

136 

137   </script>

138 </body>

139 </html>
完整代码

这里对集合的操作暂时到此,对model的操作仍然没有涉及,我们先引入路由再说吧

Control-引入路由

MVC中View的代码量可能很大,但是Control的代码往往才是核心,他全局观察着整个程序的运作,但是这里却给他换了个名字——路由

其实这个名字比较靠谱,Backbone中的控制器其实就是充当了路由的角色

路由的效果

现在我们还没看源码,不知道其实现细节,但是实现表现是这样的:

http://www.example.com/#/state1

http://www.example.com/#/state2

这里state1,和state2便是不同的View了,以我们这个例子来说,如果index是首页列表视图,detail便是某一篇博客的视图了,所以

http://www.example.com/#/index——首页列表

http://www.example.com/#/detail——具体博客

这个是路由的基本功能,想一想,假如,我们现在各个View已经写开了,view与view直接数据可以由localstorage通信

但是全局性各个状态,全局共用方法还得放到控制器里面,这里对应的就是路由里面(比如我一个全局的showLoading方法)

说了这么多也没意义,我们先将detail的view做了,再看他是怎么路由的

切换视图

其实这个代码,我处理的的有问题,我们上面的列表中的每一个列表项其实应该对应一个model

我在过程中却将model与dom的映射关系给丢了,比如我现在点击一个列表项,我需要知道我现在对应的是哪一个model

这里补救方案是给dom上增加一个索引index,点击时候根据index获取当前索引,但是这不是长久之计,后面需要更好的办法

1 for (var i = 0, len = models.length; i < len; i++) {

2   models[i].index = i;

3   html += this.itemTmpt(_.extend(models[i].toJSON(), {index: i}));

4 }

于是我们每一个dom上面就会多出一个index的属性,哎,这里感觉不是太好啊

现在点击各个列表后会给相关model增加一个model的属性:

【再探backbone 03】博客园单页应用实例(提供源码)

然后我们这里初略的重新渲染下页面,这里稍微修改下代码:

【再探backbone 03】博客园单页应用实例(提供源码)
  1 <!DOCTYPE html>

  2 <html xmlns="http://www.w3.org/1999/xhtml">

  3 <head>

  4   <meta charset="utf-8" />

  5   <title></title>

  6   <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />

  7   <link rel="Stylesheet" type="text/css" href="res/style/main2.css" />

  8   <link rel="Stylesheet" type="text/css" href="res/style/tuan.css" />

  9   <style> .pro_list_rank { margin: 5px 0; padding-right: 22px; }

 10     .figcaption span { text-align: center; }

 11     .blog_item {}

 12     .blog_item img { width: 48px; height; 48px; margin: 4px; padding: 1px; float: left; border: 1px solid #CCC;  }

 13     

 14     .blog_item .item_footer { color: #757575; font-size: 0.86em; }

 15     a { color:  #005A94; }

 16     .tab_hotel { border-left: 1px solid #2B97E2; }

 17     .cont_wrap .content { background-color: White; padding: 5px 10px; }

 18     img { max-width: 98%; }</style>

 19 </head>

 20 <body>

 21   <div class="main-frame">

 22     <div class="main-viewport" id="main-viewport">

 23     </div>

 24   </div>

 25   <script type="text/template" id="index-template">

 26   <header>

 27     <b class="icon_home i_bef" id="js_home"></b>

 28     <h1>

 29       博客园</h1>

 30     <i id="js_return" class="returnico"></i>

 31   </header>

 32   <section class="cont_wrap">

 33     <div id="post"></div>

 34     <ul class="pro_list" id="lstbox">

 35     </ul>

 36   </section>

 37   <ul class="tab_search fix_bottom" id="sort">

 38     <li class="tabcrt" attr="updated">时间</li>

 39     <li class="tab_hotel" attr="diggs">推荐</li>

 40     <li class="tab_hotel" attr="views">阅读</li>

 41     <li class="tab_hotel" attr="comments">评论</li>

 42   </ul>

 43   </script>

 44   <script type="text/template" id="item-template">

 45   <li class="arr_r orderItem" data-id="<%=id %>" data-index = "<%=index %>">

 46   <article class="blog_item">

 47     <h3>

 48       <a href="<%=link.href %>" target="_blank">

 49         <%=title.value || '无题' %></a>

 50     </h3>

 51     <div class="author pro_list_rank">

 52       <%if(author.avatar){ %>

 53       <a href="<%=author.uri %>" target="_blank">

 54         <img src="<%=author.avatar %>">

 55       </a>

 56       <%} %>

 57       <%=summary.value %>

 58     </div>

 59     <div class="item_footer">

 60       <a href="<%=author.uri %>" class="lightblue">Scut</a>

 61       <%=published %>

 62       <a href="<%=link.href %>" title="2013-08-21 15:21" class="gray">评论(<%=comments %>)</a>

 63       <a href="<%=link.href %>" class="gray">阅读(<%=views %>)</a> <span class="price1">推荐(<%=diggs %>)</span></div>

 64   </article>

 65 </li>

 66 </script>

 67   <script type="text/template" id="detail-template">

 68 <section class="cont_wrap" >

 69   <article class="content">

 70           <h1>

 71               <a href="#"><%=title.value %></a></h1>

 72               <div style=" text-align: right; ">

 73               <time pubdate="pubdate" value="2013-04-15"><%=published %></time><br /><span>阅读(<%=views %>)

 74                   评论(<%=comments %></span>

 75               </div>

 76       <p><%=value %></p>

 77   </article>

 78 </section>

 79 </script>

 80   <script src="libs/jquery.js" type="text/javascript"></script>

 81   <script src="libs/underscore.js" type="text/javascript"></script>

 82   <script src="libs/backbone.js" type="text/javascript"></script>

 83   <script type="text/javascript" src="libs/backbone.localStorage.js"></script>

 84   <script type="text/javascript">

 85     //博客模型

 86     var PostModel = Backbone.Model.extend({

 87 

 88     });

 89 

 90     //模型集合

 91     var PostList = Backbone.Collection.extend({

 92       model: PostModel,

 93       parse: function (data) {

 94         // 'data' contains the raw JSON object

 95         return (data && data.feed && data.feed.entry) || {}

 96       },

 97       setComparator: function (type) {

 98         this.comparator = function (item) {

 99           return Math.max(item.attributes[type]);

100         }

101       }

102     });

103 

104     var Detail = Backbone.View.extend({

105       template: _.template($('#detail-template').html()),

106       initialize: function () {

107 

108       },

109       render: function () {

110         this.$el.html(this.template(this.model.toJSON()));

111         return this;

112       },

113       events: {

114         'click #js_return': function () {

115           window.reload();

116         }

117       }

118     });

119 

120     var Index = Backbone.View.extend({

121       el: $('#main-viewport'),

122       template: _.template($('#index-template').html()),

123       itemTmpt: _.template($('#item-template').html()),

124 

125       events: {

126         'click #sort': function (e) {

127           var el = $(e.target);

128           var type = el.attr('attr');

129           this.list.setComparator(type);

130           this.list.sort();

131         },

132         'click .orderItem': function (e) {

133           var el = $(e.currentTarget);

134           var id = el.attr('data-id');

135           var index = el.attr('data-index');

136           var model = this.list.models[index];

137           var scope = this;

138           var param = { url: 'http://wcf.open.cnblogs.com/blog/post/body/' + id }

139           $.get('Handler.ashx', param, function (data) {

140             (typeof data === 'string') && (data = $.parseJSON(data));

141             if (data && data.string) {

142               //此处将content内容写入model

143               model.set('value', data.string.value);

144 

145               var d = new Detail();

146               d.model = model;

147               scope.wrapper.hide();

148               scope.post.append(d.render().el);

149 

150             }

151           });

152 

153 

154           var s = '';

155         }

156       },

157       initialize: function () {

158         //先生成框架html

159         this.$el.html(this.template());

160         this.post = this.$('#post');

161 

162         var scope = this;

163         var curpage = 1;

164         var pageSize = 10;

165         this.list = new PostList();

166         this.list.url = 'Handler.ashx?url=http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize;

167         this.list.fetch({

168           success: function () {

169             scope.render();

170           }

171         });

172         this.wrapper = $('#lstbox');

173 

174         this.listenTo(this.list, 'all', this.render);

175 

176       },

177       render: function () {

178 

179         var models = this.list.models;

180         var html = '';

181         for (var i = 0, len = models.length; i < len; i++) {

182           models[i].index = i;

183           html += this.itemTmpt(_.extend(models[i].toJSON(), { index: i }));

184         }

185         this.wrapper.html(html);

186         var s = '';

187       }

188     });

189 

190     var index = new Index();

191 //    var detail 

192 

193 

194     var s = '';

195 

196   </script>

197 </body>

198 </html>
完整的代码

这个代码里面,我们点击其中一个list-item就会显示我们博客正文,但是问题马上就来了

【再探backbone 03】博客园单页应用实例(提供源码)

现在的问题

做到现在我们程序大概的逻辑也出来了,当然,问题也出来了:

① 我从首页来到了博客页,但是大哥,我该怎么回去啊???

② 我们的程序全部在demo这个页面里面,个人感觉很乱啊!!!现在是两个view就这样,万一我有20个view我是不是要哭!!!

③ 我们最开始的view时index的,比如上面的回退按钮与“博客园”三个字,我们在博客页整个view应该隶属于自己的,但是这是嵌套的,很乱

要解决以上问题,我觉得是时候引入require已经路由的知识点了

require这个库,我们后面有时间在一起深入学习下,这里只是使用其核心的功能,简单起见,模板我就全部写在demo中,不予分离了

引入路由功能

我们当然想在页面中点击某个按钮来导向某个方法了,比如我们上面进入了detail博客页,我现在先回到首页,我可以后退

 1 var App = Backbone.Router.extend({

 2   routes: {

 3     "": "index",    // #index

 4     "index": "index",    // #index

 5     "detail": "detail"    // #detail

 6   },

 7   index: function () {

 8     var index = new Index();

 9   }

10 });

11 

12 var app = new App();

13 Backbone.history.start();

这么小心一段代码加上后,就会在页面url锚点#后面的字符变了后执行相关的方法,显然我们这里需要更多的信息,或者说,我们的路由应该担任整个view通信的功能

首先,我们这里将index与detail两个view的模板完全独立,这里用了比较图的办法,全部清除html,但是现在确实可以跳转了

【再探backbone 03】博客园单页应用实例(提供源码)
  1 <!DOCTYPE html>

  2 <html xmlns="http://www.w3.org/1999/xhtml">

  3 <head>

  4   <meta charset="utf-8" />

  5   <title></title>

  6   <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />

  7   <link rel="Stylesheet" type="text/css" href="res/style/main2.css" />

  8   <link rel="Stylesheet" type="text/css" href="res/style/tuan.css" />

  9   <style> .pro_list_rank { margin: 5px 0; padding-right: 22px; }

 10     .figcaption span { text-align: center; }

 11     .blog_item {}

 12     .blog_item img { width: 48px; height; 48px; margin: 4px; padding: 1px; float: left; border: 1px solid #CCC;  }

 13     

 14     .blog_item .item_footer { color: #757575; font-size: 0.86em; }

 15     a { color:  #005A94; }

 16     .tab_hotel { border-left: 1px solid #2B97E2; }

 17     .cont_wrap .content { background-color: White; padding: 5px 10px; }

 18     img { max-width: 98%; }</style>

 19 </head>

 20 <body>

 21   <h1>

 22     <a href="demo.htm#index">index</a>

 23   </h1>

 24   <div class="main-frame">

 25     <div class="main-viewport" id="main-viewport">

 26     </div>

 27   </div>

 28   <script type="text/template" id="index-template">

 29   <header>

 30     <b class="icon_home i_bef" id="js_home"></b>

 31     <h1>

 32       博客园</h1>

 33 

 34     <i id="js_return" class="returnico"></i>

 35   </header>

 36   <section class="cont_wrap">

 37     <div id="post"></div>

 38     <ul class="pro_list" id="lstbox">

 39     </ul>

 40   </section>

 41   <ul class="tab_search fix_bottom" id="sort">

 42     <li class="tabcrt" attr="updated">时间</li>

 43     <li class="tab_hotel" attr="diggs">推荐</li>

 44     <li class="tab_hotel" attr="views">阅读</li>

 45     <li class="tab_hotel" attr="comments">评论</li>

 46   </ul>

 47   </script>

 48   <script type="text/template" id="item-template">

 49   <li class="arr_r orderItem" data-id="<%=id %>" data-index = "<%=index %>">

 50   <article class="blog_item">

 51     <h3>

 52       <a href="<%=link.href %>" target="_blank">

 53         <%=title.value || '无题' %></a>

 54     </h3>

 55     <div class="author pro_list_rank">

 56       <%if(author.avatar){ %>

 57       <a href="<%=author.uri %>" target="_blank">

 58         <img src="<%=author.avatar %>">

 59       </a>

 60       <%} %>

 61       <%=summary.value %>

 62     </div>

 63     <div class="item_footer">

 64       <a href="<%=author.uri %>" class="lightblue">Scut</a>

 65       <%=published %>

 66       <a href="<%=link.href %>" title="2013-08-21 15:21" class="gray">评论(<%=comments %>)</a>

 67       <a href="<%=link.href %>" class="gray">阅读(<%=views %>)</a> <span class="price1">推荐(<%=diggs %>)</span></div>

 68   </article>

 69 </li>

 70 </script>

 71   <script type="text/template" id="detail-template">

 72 <section class="cont_wrap" >

 73   <article class="content">

 74           <h1>

 75               <a href="#"><%=title.value %></a></h1>

 76               <div style=" text-align: right; ">

 77               <time pubdate="pubdate" value="2013-04-15"><%=published %></time><br /><span>阅读(<%=views %>)

 78                   评论(<%=comments %></span>

 79               </div>

 80       <p><%=value %></p>

 81   </article>

 82 </section>

 83 </script>

 84   <script src="libs/jquery.js" type="text/javascript"></script>

 85   <script src="libs/underscore.js" type="text/javascript"></script>

 86   <script src="libs/backbone.js" type="text/javascript"></script>

 87   <script type="text/javascript" src="libs/backbone.localStorage.js"></script>

 88   <script type="text/javascript">

 89     //博客模型

 90     var PostModel = Backbone.Model.extend({

 91 

 92     });

 93 

 94     //模型集合

 95     var PostList = Backbone.Collection.extend({

 96       model: PostModel,

 97       parse: function (data) {

 98         // 'data' contains the raw JSON object

 99         return (data && data.feed && data.feed.entry) || {}

100       },

101       setComparator: function (type) {

102         this.comparator = function (item) {

103           return Math.max(item.attributes[type]);

104         }

105       }

106     });

107 

108     var Detail = Backbone.View.extend({

109       el: $('#main-viewport'),

110       template: _.template($('#index-template').html()),

111       detail: _.template($('#detail-template').html()),

112       initialize: function () {

113         this.$el.html(this.template());

114         this.wrapper = $('#lstbox');

115 

116       },

117       render: function () {

118         this.wrapper.html(this.detail(this.model.toJSON()));

119       },

120       events: {

121         'click #js_return': function () {

122           location.href = "demo.htm";

123         }

124       }

125     });

126 

127     var Index = Backbone.View.extend({

128       el: $('#main-viewport'),

129       template: _.template($('#index-template').html()),

130       itemTmpt: _.template($('#item-template').html()),

131 

132       events: {

133         'click #sort': function (e) {

134           var el = $(e.target);

135           var type = el.attr('attr');

136           this.list.setComparator(type);

137           this.list.sort();

138         },

139         'click .orderItem': function (e) {

140           var el = $(e.currentTarget);

141           var id = el.attr('data-id');

142           var index = el.attr('data-index');

143           var model = this.list.models[index];

144           var scope = this;

145           var param = { url: 'http://wcf.open.cnblogs.com/blog/post/body/' + id }

146           $.get('Handler.ashx', param, function (data) {

147             (typeof data === 'string') && (data = $.parseJSON(data));

148             if (data && data.string) {

149               //此处将content内容写入model

150               model.set('value', data.string.value);

151               var d = new Detail();

152               d.model = model;

153               d.render();

154             }

155           });

156           var s = '';

157         }

158       },

159       initialize: function () {

160         //先生成框架html

161         this.$el.html(this.template());

162         this.post = this.$('#post');

163 

164         var scope = this;

165         var curpage = 1;

166         var pageSize = 10;

167         this.list = new PostList();

168         this.list.url = 'Handler.ashx?url=http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize;

169         this.list.fetch({

170           success: function () {

171             scope.render();

172           }

173         });

174         this.wrapper = $('#lstbox');

175 

176         this.listenTo(this.list, 'all', this.render);

177 

178       },

179       render: function () {

180 

181         var models = this.list.models;

182         var html = '';

183         for (var i = 0, len = models.length; i < len; i++) {

184           models[i].index = i;

185           html += this.itemTmpt(_.extend(models[i].toJSON(), { index: i }));

186         }

187         this.wrapper.html(html);

188         var s = '';

189       }

190     });

191 

192     //    var index = new Index();

193     //    var detail

194 

195     var App = Backbone.Router.extend({

196       routes: {

197         "": "index",    // #index

198         "index": "index",    // #index

199         "detail": "detail"    // #detail

200       },

201       index: function () {

202         var index = new Index();

203       }

204     });

205 

206     var app = new App();

207     Backbone.history.start();

208 

209     var s = '';

210 

211   </script>

212 </body>

213 </html>
View Code

现在问题又来了,我点击其中一个一个列表项目时候,我就执行了render方法,现在我不想这么做了,我想将其加载数据的逻辑放到detail里面去

并且通过路由的方式实现,但是我们数据model的传递(这里没有使用localstorage)需要用路由来实现

【再探backbone 03】博客园单页应用实例(提供源码)
  1 <!DOCTYPE html>

  2 <html xmlns="http://www.w3.org/1999/xhtml">

  3 <head>

  4   <meta charset="utf-8" />

  5   <title></title>

  6   <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />

  7   <link rel="Stylesheet" type="text/css" href="res/style/main2.css" />

  8   <link rel="Stylesheet" type="text/css" href="res/style/tuan.css" />

  9   <style> .pro_list_rank { margin: 5px 0; padding-right: 22px; }

 10     .figcaption span { text-align: center; }

 11     .blog_item {}

 12     .blog_item img { width: 48px; height; 48px; margin: 4px; padding: 1px; float: left; border: 1px solid #CCC;  }

 13     

 14     .blog_item .item_footer { color: #757575; font-size: 0.86em; }

 15     a { color:  #005A94; }

 16     .tab_hotel { border-left: 1px solid #2B97E2; }

 17     .cont_wrap .content { background-color: White; padding: 5px 10px; }

 18     img { max-width: 98%; }</style>

 19 </head>

 20 <body>

 21   <div class="main-frame">

 22     <div class="main-viewport" id="main-viewport">

 23     </div>

 24   </div>

 25   <script type="text/template" id="index-template">

 26   <header>

 27     <b class="icon_home i_bef" id="js_home"></b>

 28     <h1>

 29       博客园</h1>

 30 

 31     <i id="js_return" class="returnico"></i>

 32   </header>

 33   <section class="cont_wrap">

 34     <div id="post"></div>

 35     <ul class="pro_list" id="lstbox">

 36     </ul>

 37   </section>

 38   <ul class="tab_search fix_bottom" id="sort">

 39     <li class="tabcrt" attr="updated">时间</li>

 40     <li class="tab_hotel" attr="diggs">推荐</li>

 41     <li class="tab_hotel" attr="views">阅读</li>

 42     <li class="tab_hotel" attr="comments">评论</li>

 43   </ul>

 44   </script>

 45   <script type="text/template" id="item-template">

 46   <li class="arr_r orderItem" data-id="<%=id %>" data-index = "<%=index %>">

 47   <article class="blog_item">

 48     <h3>

 49       <a href="<%=link.href %>" target="_blank">

 50         <%=title.value || '无题' %></a>

 51     </h3>

 52     <div class="author pro_list_rank">

 53       <%if(author.avatar){ %>

 54       <a href="<%=author.uri %>" target="_blank">

 55         <img src="<%=author.avatar %>">

 56       </a>

 57       <%} %>

 58       <%=summary.value %>

 59     </div>

 60     <div class="item_footer">

 61       <a href="<%=author.uri %>" class="lightblue">Scut</a>

 62       <%=published %>

 63       <a href="<%=link.href %>" title="2013-08-21 15:21" class="gray">评论(<%=comments %>)</a>

 64       <a href="<%=link.href %>" class="gray">阅读(<%=views %>)</a> <span class="price1">推荐(<%=diggs %>)</span></div>

 65   </article>

 66 </li>

 67 </script>

 68   <script type="text/template" id="detail-template">

 69 <section class="cont_wrap" >

 70   <article class="content">

 71           <h1>

 72               <a href="#"><%=title.value %></a></h1>

 73               <div style=" text-align: right; ">

 74               <time pubdate="pubdate" value="2013-04-15"><%=published %></time><br /><span>阅读(<%=views %>)

 75                   评论(<%=comments %></span>

 76               </div>

 77       <p><%=value %></p>

 78   </article>

 79 </section>

 80 </script>

 81   <script src="libs/jquery.js" type="text/javascript"></script>

 82   <script src="libs/underscore.js" type="text/javascript"></script>

 83   <script src="libs/backbone.js" type="text/javascript"></script>

 84   <script type="text/javascript" src="libs/backbone.localStorage.js"></script>

 85   <script type="text/javascript">

 86     //博客模型

 87     var PostModel = Backbone.Model.extend({

 88 

 89     });

 90 

 91     //模型集合

 92     var PostList = Backbone.Collection.extend({

 93       model: PostModel,

 94       parse: function (data) {

 95         // 'data' contains the raw JSON object

 96         return (data && data.feed && data.feed.entry) || {}

 97       },

 98       setComparator: function (type) {

 99         this.comparator = function (item) {

100           return Math.max(item.attributes[type]);

101         }

102       }

103     });

104 

105     var Detail = Backbone.View.extend({

106       el: $('#main-viewport'),

107       template: _.template($('#index-template').html()),

108       detail: _.template($('#detail-template').html()),

109       initialize: function (app) {

110         this.app = app;

111         this.$el.html(this.template());

112         this.wrapper = $('#lstbox');

113         this.render();

114       },

115       render: function () {

116         var scope = this;

117         var id = this.app.id;

118 

119         var param = { url: 'http://wcf.open.cnblogs.com/blog/post/body/' + id }

120 

121         var model = this.app.model;

122 

123         $.get('Handler.ashx', param, function (data) {

124           (typeof data === 'string') && (data = $.parseJSON(data));

125           if (data && data.string) {

126             //此处将content内容写入model

127             model.set('value', data.string.value);

128             scope.wrapper.html(scope.detail(model.toJSON()));

129           }

130         });

131 

132       },

133       events: {

134         'click #js_return': function () {

135           this.app.forward('index')

136         }

137       }

138     });

139 

140     var Index = Backbone.View.extend({

141       el: $('#main-viewport'),

142       template: _.template($('#index-template').html()),

143       itemTmpt: _.template($('#item-template').html()),

144 

145       events: {

146         'click #sort': function (e) {

147           var el = $(e.target);

148           var type = el.attr('attr');

149           this.list.setComparator(type);

150           this.list.sort();

151         },

152         'click .orderItem': function (e) {

153           var el = $(e.currentTarget);

154           var index = el.attr('data-index');

155           var id = el.attr('data-id');

156           var model = this.list.models[index];

157           this.app.model = model;

158           this.app.id = id;

159 

160 

161           this.app.forward('detail');

162           //          var scope = this;

163           //          var param = { url: 'http://wcf.open.cnblogs.com/blog/post/body/' + id }

164           //          $.get('Handler.ashx', param, function (data) {

165           //            (typeof data === 'string') && (data = $.parseJSON(data));

166           //            if (data && data.string) {

167           //              //此处将content内容写入model

168           //              model.set('value', data.string.value);

169           //              var d = new Detail();

170           //              d.model = model;

171           //              d.render();

172           //            }

173           //          });

174           //          var s = '';

175         }

176       },

177       initialize: function (app) {

178         this.app = app;

179 

180         //先生成框架html

181         this.$el.html(this.template());

182         this.post = this.$('#post');

183 

184         var scope = this;

185         var curpage = 1;

186         var pageSize = 10;

187         this.list = new PostList();

188         this.list.url = 'Handler.ashx?url=http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize;

189         this.list.fetch({

190           success: function () {

191             scope.render();

192           }

193         });

194         this.wrapper = $('#lstbox');

195 

196         this.listenTo(this.list, 'all', this.render);

197 

198       },

199       render: function () {

200 

201         var models = this.list.models;

202         var html = '';

203         for (var i = 0, len = models.length; i < len; i++) {

204           models[i].index = i;

205           html += this.itemTmpt(_.extend(models[i].toJSON(), { index: i }));

206         }

207         this.wrapper.html(html);

208         var s = '';

209       }

210     });

211 

212     //    var index = new Index();

213     //    var detail

214 

215     var App = Backbone.Router.extend({

216       routes: {

217         "": "index",    // #index

218         "index": "index",    // #index

219         "detail": "detail"    // #detail

220       },

221       index: function () {

222         var index = new Index(this.interface);

223 

224       },

225       detail: function () {

226         var detail = new Detail(this.interface);

227 

228       },

229       initialize: function () {

230 

231       },

232       interface: {

233         forward: function (url) {

234           window.location.href = ('#' + url).replace(/^#+/, '#');

235         }

236 

237       }

238 

239 

240     });

241 

242     var app = new App();

243     Backbone.history.start();

244 

245     var s = '';

246 

247   </script>

248 </body>

249 </html>
重要版本

如此一来,我们的基本逻辑结束,马上我们又面临其它问题:

① 我们每个view的initialize方法都会被传入app接口

② 我们每个view都有一些重复的代码(比如this.app = app),而我们各个view没有好的状态控制

比如我们页面的create阶段,load阶段,show阶段,hide阶段,这些都需要事件进行处理,这里却没法一一关注

③ 可以看到,我们的路由居然还充当了很多传递数据的作用,但是我们并不应该这样写

④ 各位是否感觉我们的路由仍然太简陋,我们的程序很容易报错呢?

以上这些问题暂时留给各位思考,我下次使用require将我们的view分离先,本来今天想做的,但是感觉干不动了

结语

今天的内容暂时到此,本来想做个简单的例子来着,结果做着做着居然又涉及到这么多东西了,我们下次将这个单页应用完成,并且开始源码学习:

① 细化App(路由/控制器),让其完成更多的工作

② 引入require分离view

③ 创建view父类供各个view使用

④ 源码细节实现分析

总的来说,Backbone的阅读应该比原来碰到的框架难,原来那些框架只不过是基础库,但是Backbone涉及到了思想

这种框架性东西,我们后面还得花大力气消化,今天暂时到此

你可能感兴趣的:(backbone)