本文轉載自:http://www.cnblogs.com/kenkofox/p/4650310.htmlhtml
不過,這一篇,我想進一步探討一下這兩個框架的優缺點,另外,再進一步,拋開這兩個框架,回到本真,本身搞個簡單的路由同樣能夠實現單頁面。前端
這個對於剛作前端開發的新同窗來講就最好不過了,若是一來到崗位就一大堆angular、backbone、requirejs,看資料都看一兩週。其實你們最熟悉的東西仍是那個美圓$,用美圓能解決的問題,就不要麻煩到angular、backbone大爺了。jquery
事先說明,因爲個人業務範圍窄,不必定能把angular和backbone的功能都用一遍,因此如下的分析可能以偏概全,歡迎你們討論。git
angular優勢:github
angular缺點:web
backbone優勢:正則表達式
backbone缺點:閉包
其實,這兩個框架都很是優秀,可是,在實際業務中,不必定百試百靈,由於有一些移動端的單頁面web,業務就很簡單,只是路由分別切換到幾個子模塊,每一個子模塊基本都是拉一次數據,展現給用戶,不多用戶交互從而修改數據,改變視圖的功能。架構
對於這種狀況,使用angular未免有點殺雞用牛刀的感受,而backbone雖然小巧了很多,可是模型的功能也是浪費的。app
因此,在這裏,我想探討一下,可否拋開這兩個框架,只索取咱們基本所需,創建一個更簡單的架構呢?
經驗看來,一些類庫是必不可少的:
本身作一套最簡單的架構,思想很是簡單:
簡單的思路,讓架構很是簡潔明瞭,新團隊成員來到可以輕鬆上手,而angular和backbone的架構,少說得二、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