天天看點

【再探backbone 03】部落格園單頁應用執行個體(提供源碼)

前言

之前嘗試性的讀了下backbone的源碼,對繼承、事件、view一塊還比較熟悉,看下去比較順暢,但是在model collection方面就很不順利

究其原因還是沒有使用過,不夠熟悉,以碼讀碼,脫離了業務邏輯的代碼毫無意義,是以今天我們先來做一個例子吧,然後再根據例子學習

今天來一段官網以外的代碼,本來這裡想抄一個代碼來着,但是這樣的話好像意義就不大了,于是自己重新寫一個例子吧

注意這個例子隻是簡單例子,各位不要當真,而且代碼是今天下午寫的,有BUG不要罵我,然後放到IIS環境下才有資料

下載下傳源碼:02backbone.zip

單頁應用執行個體

以部落格園為例,我們一起做一個單頁,提供list和detail兩個頁面,并且為list提供分頁和簡單篩選功能來鞏固我們的backbone學習

檔案結構

首先為了簡單起見,我們這裡目錄暫時如下:

其中css樣式是上次做單頁應用研究剩下的,這次直接拿來用吧

資料源

然後,我們來簡單看看我們部落格園的資料源傳回的資料:

很遺憾部落格園暫時隻提供了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 = '';

  }

});

需要注意一點是這裡是異步的,是以不要傻傻的馬上想在後面操作資料

視圖-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就出來了:

附上完整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');

6     var s = '';

7   }

8 },

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重新渲染了下資料

 完整代碼

這裡對集合的操作暫時到此,對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的屬性:

然後我們這裡初略的重新渲染下頁面,這裡稍微修改下代碼:

 完整的代碼

這個代碼裡面,我們點選其中一個list-item就會顯示我們部落格正文,但是問題馬上就來了

現在的問題

做到現在我們程式大概的邏輯也出來了,當然,問題也出來了:

① 我從首頁來到了部落格頁,但是大哥,我該怎麼回去啊???

② 我們的程式全部在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,但是現在确實可以跳轉了

 View Code

現在問題又來了,我點選其中一個一個清單項目時候,我就執行了render方法,現在我不想這麼做了,我想将其加載資料的邏輯放到detail裡面去

并且通過路由的方式實作,但是我們資料model的傳遞(這裡沒有使用localstorage)需要用路由來實作

 重要版本

如此一來,我們的基本邏輯結束,馬上我們又面臨其它問題:

① 我們每個view的initialize方法都會被傳入app接口

② 我們每個view都有一些重複的代碼(比如this.app = app),而我們各個view沒有好的狀态控制

比如我們頁面的create階段,load階段,show階段,hide階段,這些都需要事件進行處理,這裡卻沒法一一關注

③ 可以看到,我們的路由居然還充當了很多傳遞資料的作用,但是我們并不應該這樣寫

④ 各位是否感覺我們的路由仍然太簡陋,我們的程式很容易報錯呢?

以上這些問題暫時留給各位思考,我下次使用require将我們的view分離先,本來今天想做的,但是感覺幹不動了

結語

今天的内容暫時到此,本來想做個簡單的例子來着,結果做着做着居然又涉及到這麼多東西了,我們下次将這個單頁應用完成,并且開始源碼學習:

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

② 引入require分離view

③ 建立view父類供各個view使用

④ 源碼細節實作分析

總的來說,Backbone的閱讀應該比原來碰到的架構難,原來那些架構隻不過是基礎庫,但是Backbone涉及到了思想

這種架構性東西,我們後面還得花大力氣消化,今天暫時到此

本文轉自葉小钗部落格園部落格,原文連結:http://www.cnblogs.com/yexiaochai/p/3486173.html,如需轉載請自行聯系原作者

繼續閱讀