backbone.js的使用報告

一、準備階段

1.1 框架選型

隨著對MV*架構模式的逐步理解,越來越發覺對于一般的業務場景,mvvm是前端架構的不二選擇。在我所了解的框架范圍內,與mvvm架構模式最貼合的框架只有angular,無論是雙向數據綁定、指令,還有強化的html標簽,angular提出的很多新概念都像是為mvvm理論量身定做的一套外衣。很可惜angular對ie8的支持不太友好,兼容ie8的angular插件也非常稀缺,對于國內環境,ie8仍然占有較大的市場份額,無奈之下只能轉向輕量級的backbone。

1.2 官方文檔vs電子書

通過閱讀backbone的官網簡介,總結下backbone特點(并非優點):

  • 提供了基本的MVC結構,可以做到業務邏輯與視圖的分離(MV*框架最基本的功能)
  • 內置支持restful風格的API(做過項目后會發覺,內置的那套基本沒什么用,自定義會更合適)
  • 路由的支持(大部分MV*框架也都支持,也不是亮點)
  • 方便與第三方插件集成(因為backbone太輕量了,對于插件沒什么要求,這勉強可以算優點)
  • 兼容ie8(對比其他MV*框架,感覺這是唯一的優點,也是選擇它的理由)
  • 粗粒度的單向數據綁定(缺點,每次更新視圖,都只能全部更新,當然你可以自己寫局部更新,會比較繁瑣)
  • 太輕量了(缺點,連angular這種重量級的框架寫法都會五花八門,backbone這種就更別提了,新手根本無法駕馭它寫出優良的代碼)

由于backbone功能單薄,無法形成固有的mvc或是mvp模式,但是為了讓它變得好用,我們可以嘗試增強它的功能,讓它更接近我們想要的mvvm框架。mvvm框架最大特點是:雙向數據綁定,于是通過google:backbone data binding,找到了以下幾個backbone插件:Backbone.ModelBinder、Rivets.js、Backbone.Stickit。通過測試發現Rivets.js很好用,可惜不兼容ie8,ModelBinder配置不夠靈活,于是Stickit成為最佳選擇。

關于框架的學習,我的思路是這樣的:官方文檔是一定要看的,但是沒必要從頭看到尾,官方開頭那部分介紹是一定要看的,那里會告訴你框架是什么樣的,有什么特性,而具體的API只要看能反應出框架特性的那幾個API就夠了,其他的都是用來查的。有些API不用查,你也知道它肯定有,就像你學完java后學C++,你知道C++里肯定有for循環。
<b>只看官網文檔是遠遠不夠的,文檔只是告訴你可以這樣用,但是沒有告訴你該不該這樣用,所以當你大體了解了一個框架的基礎知識后,應該找一本名叫《xxx框架最佳實踐》的電子書,了解下怎么用才合理,對于輕量級的框架尤其如此。</b>

學計算機知識,一個wiki百科就夠了(要翻墻),千萬不要看國內某百科,連自己貼吧的內容都可以當參考文獻,實在是太不靠譜了。

二、實踐階段

準備階段做得越多,實踐起來就越輕松,項目需求分析階段,開發人員的時間如果用來做技術調研、框架選型、編寫demo、測試性能、編寫非業務組件等工作,時間還是會比較緊張的。總結了一下項目中的問題及解決方案:

2.1 如何編寫model層代碼

對于mvc新手,經常會誤解m的含義,認為m只是表示數據。實際上mvc是按照職責來劃分的,而非數據類型。m表示業務層,即包含表示業務邏輯的數據模型,同時也包含操作這些數據的方法。反應到backbone項目中,m層的寫法如下:

var Product = Backbone.Model.extend({

    // 業務數據模型
    defaults: {
        name: “”,
        price: 0
    },
    
    // 操作數據的方法
    create: function() {...},
    remove: function() {...},
    modify: function() {...},
    query: function() {...}
});

不要在view中直接調用沒有業務含義的底層方法:fetch、save等,這將導致model與view耦合在一起,view層的代碼變得繁雜且難以維護。

2.2 事件與邏輯分離

backbone提供了View類,很多人并沒有意識到事件與邏輯的解耦,他們通常這樣寫:

var ProductView = Backbone.View.extend({

    // 注冊事件
    events: {
        "click #saveBtn": "create",
        ....
    },
    
    // 創建
    create: function() {

        // 針對click準備一些數據,可能涉及到dom操作
        var data = ....;
        
        // 執行創建的邏輯
        ...
    }
});

當觸發創建邏輯的事件不止一個時,會變成這樣:

var ProductView = Backbone.View.extend({

    // 注冊事件
    events: {
        "click #saveBtn": "createForClick",
        "blur #xxInput": "createForBlur",
        ....
    },
    
    // 創建for click
    createForClick: function() {

        // 針對click準備一些數據,可能涉及到dom操作
        var data = ....;
        
        // 執行創建的邏輯
        ...
    },

    // 創建for blur
    createForBlur: function() {

        // 針對blur準備一些數據,可能涉及到dom操作
        var data = ....;
        
        // 執行創建的邏輯
        ...
    }
});

此時你會發現處理邏輯的代碼重復了,所以將事件與邏輯分離的一個優點是:邏輯代碼可以復用,正確的寫法如下:

var ProductView = Backbone.View.extend({

    // 注冊事件
    events: {
        "click #saveBtn": "createForClick",
        "blur #xxInput": "createForBlur",
        ....
    },
    
    // 響應click事件的創建
    createForClick: function() {

        // 針對click準備一些數據,可能涉及到dom操作
        var data = ....;
        
        // 執行創建的邏輯
        this.create(data);
    },

    // 響應blur事件的創建
    createForBlur: function() {

        // 針對blur準備一些數據,可能涉及到dom操作
        var data = ....;
        
        // 執行創建的邏輯
        this.create(data);
    },

    // 執行創建
    create: function(data) {...}
});

事件與邏輯的分離最大的好處是可以方便的進行單元測試,以上面為例,只需針對create方法進行測試,就能驗證邏輯的正確性,而非邏輯的dom操作是不在單元測試范圍之內的。

2.3 凈化路由代碼

backbone提供了路由功能,可以方便的根據網址跳轉到指定的視圖,很多人的寫法是這樣的:

var AppRouter = Backbone.Router.extend({
    "route1": "createView1",
    "route2": "createView2",
    ....,

    createView1: function() {
        // 1.操作model層方法,獲取視圖所需數據
        ...
        // 2.new一個視圖對象,傳遞數據到視圖中
        ...
        // 3.其他的邏輯
        ...
    },

    createView2: function() {
        // 1.操作model層方法,獲取視圖所需數據
        ...
        // 2.new一個視圖對象,傳遞數據到視圖中
        ...
        // 3.其他的邏輯
        ...
    }
});

隨著需求的增加,創建視圖的邏輯會變的越來越復雜,整個路由的代碼會顯得非常臃腫,還有一個問題,不單單路由里需要創建視圖,其他的地方也會用到,這個時候,重復的代碼又出現了。解決這個問題的方案是抽取出創建視圖的邏輯,實際項目中我抽取了一個文件夾叫作:controllers,里面以業務功能為單位,存放創建視圖邏輯的js文件,比如處理產品的controller,可以定義為productCtrl.js,代碼如下:

var productCtrl= (function() {
    var create = function() {
        // 1.操作model層方法,獲取視圖所需數據
        ...
        // 2.new一個視圖對象,傳遞數據到視圖中
        ...
        // 3.其他的邏輯
        ...
    };

    return {
        create: create
    };
});

此時路由的代碼就變得非常清爽了,同時其他地方需要創建視圖時,只需要調用productCtrl.create()即可,路由代碼:

var AppRouter = Backbone.Router.extend({
    "route1": "createView1",
    "route2": "createView2",
    ....,

    createView1: function() {
        productCtrl.create();
    },

    createView2: function() {
        ...
    }
});
2.4 引入bower-installer優化bower文件結構

前端構建時需要合并第三方的js、css文件,但是每個插件的目錄結構不盡相同,導致grunt命令寫起來非常繁瑣,為了盡量統一處理,引入了bower-installer插件,主要目的是抽取出插件的核心文件,將其放入規則統一的目錄中,方便進一步處理。 bower-installer的github地址:https://github.com/blittle/bower-installer

2.4.1 bower-installer使用
  • 安裝:npm install -g bower-installer
  • 運行:bower-installer
    具體配置參考github上的文檔
2.4.2 基于bower-installer引入第三方插件的流程(以jquery為例)
  1. 配置bower.json文件:"jquery": "1.11.3"
  2. 執行bower install下載jquery插件
  3. 執行bower installer抽取jquery核心文件到bower_main_files目錄
  4. 引入文件:在index.html中引入插件的根目錄由bower_components改為bower_main_files
2.5 mvvm的缺陷

開發過程一切都很順利,只踩到兩個坑:<b>性能</b>、<b>復雜界面的邏輯處理</b>,而這兩個坑是由mvvm的基因決定的。

mvvm的雙向數據綁定可以讓開發變得非常便利,但同時也帶來了兩個問題:

  • 為了將model與dom中的元素一一對應進行綁定,即便最簡單的界面,也會消耗大量性能,當需要一次性加載大量數據時,性能問題就會凸顯。
  • 數據綁定是基于觀察者模式構建的,當界面中存在多個組件,且組件間有復雜交互時,代碼很難跟蹤,調試會變得非常困難,當修改了某個model時,你無法清晰的了解后面會發生什么。

針對mvvm的缺點,以下業務場景是不適合的:

  1. 由于極端的用戶體驗要求,需要一次性渲染大量數據,不能分頁的場景。
  2. 組件多且交互過復雜的場景。

據說angular2.0也傾向單向數據流,估計也是考慮到這個原因,react+flux也推崇單向數據流,也許這是一種趨勢。但對于一般的業務場景來說,雙向數據綁定還是非常實用的,所以具體采用什么方案還是要結合具體的業務場景。

三、總結

  • backbone.js是一個功能單薄的框架,它需要其他插件的輔助才能變得好用。
  • 沒有了解過xxx最佳實踐,就不要輕易在項目里使用,不然往往會得到xxx不好用的錯誤結論。
  • 沒有一個模式永遠是最佳的,只有適合業務場景的模式才是最佳模式。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,662評論 25 708
  • 彼岸花開開彼岸
    柒染韻墨閱讀 263評論 0 0
  • 魚游在水里 有了魚水之情 魚游進了巖石之腹 再也不曾離開 這是幸運 還是緣分 愛上一個人 因為深刻 所以永恒
    足下阿蒙閱讀 98評論 0 0
  • 大過年的人家都是寫要好好過年,新年有什么好計劃等等之類的,為什么我要寫什么難的糊涂呢,好像這個詞不該過年時候說,或...
    才少說閱讀 112評論 0 0
  • 背景:1. 本地windows系統已有開發指定版本的jdk安裝包,已有tomcat解壓縮后的程序文件。無需另外下載...
    Joey_GZ閱讀 649評論 1 2