Riot.js — 1Kb 大小的 JavaScript 的 MVP 框架
一個可以構建大型網絡應用并令人難以置信快和強大但輕量級的客戶端框架
Riot.js是一個客戶端模型-視圖-呈現(MVP)框架并且它非常輕量級甚至小于1kb.盡管他的大小令人難以置信,所有它能構建的有如下:一個模板引擎,路由,甚至是庫和一個嚴格的并具有組織的MVP模式。當模型數據變化時視圖也會自動更新。
Riot.js快而且簡單-事實上,是完全不同的規模-而且用它的應用也很快簡單。真的。
讓我詳細解釋吧。
最快
Riot.js是目前存在的JavaScript模板引擎中速度最快的。它比Resig的“微模板”快5倍,比Underscore快7倍。
在你應用中的模板影響著渲染時間。模板快可以讓你的應用快。在大型應用中影響是很大的,特別是在非Webkit瀏覽器中。上述在火狐中用100K重復的測試可在Hogan花費1300ms而在Riot中耗時30ms。請在不同的瀏覽器中做做測試可以得到更好的結果。
最輕
下面是Riot和其它流行的客戶端框架文件大小比較:
很明顯,Riot的是最小的,而且框架的大小會影響到以下幾個方面:
- 容易學習。需要看更少的書和指導。這種不同點影響是巨大的:想想看3個API和300個API的區別。你需要更多的時間來構建應用。這基本上是給大團隊用的。錯誤的決定會導致整個工程失敗。
- 更少專有語法,更多是直接用JavaScript。用通用的編程技巧代替一些框架自己的規則是很重要的。
- 更少的問題。更不容易受到攻擊和發現弱點。所有的錯誤都是用戶產生的問題點。令人沮喪的是有時候你的應用失敗很有可能是因為框架引起的。
- 易嵌入。在哪里用Riot.js都不會讓人覺得多余。框架不能比應用本身的代碼量大。
- 更快。根據Google的報告,每增加1kb的JavaScript,瀏覽器就多花費1ms的時間來解析(1)。
- 更節約。對于Amazon.com,每增加100ms的加載JavaScript時間會讓其稅收增加1%(2)。在2012年,Amazon 1%的稅收總共$610.9百萬。 </ol>
- emit(event_name, args...)— 帶有可選參數并能觸發一個命名事件
- on(event_name, fn)— 當一個特定事件發生時調用給定的函數
- one(event_name, fn)— 當一個特定事件觸發時調用給定函數一次,額外的事件將沒有作用
- off(event_name)— 停止監聽指定的事件 </ul>
- JavaScript要在CSS之外
- CSS要在JavaScript之外
- JavaScript要在HTML之外
- HTML要在JavaScript之外 </ol>
- 存在一大塊常見的問題
- MVC(或MVP)需要一個框架
- jQuery讓代碼更加混亂 </ol>
如果你的工具是其它消費應用而用的,那么其大小可能更加重要。
最強大
這是一個令人震驚的部分:一個僅有1Kb的程序庫創建一個待辦事項的MVC應用所需的代碼量:

應用所需代碼量的圖片傳達出了一個好框架的能力。一個框架的全部意義在于解決公共問題,這樣用戶就不必重新發明輪子。寫盡量少的代碼但能實現更多功能。
代碼量的大小取決于多種因素,比如編程風格,所以老實說你的目標不應該是代碼量小,而是簡單。
簡潔是Riot真正閃亮的地方。
MVP設計模式
Riot使用Model-View-Presenter (MVP)設計模式來組織代碼,這樣它能夠更模塊化、更具可測試性且易于理解。
正如在MVC(模型-視圖-控制器)或MVVM(模型-視圖-視圖模型)模式里,其目的是從應用程序的視圖中分離邏輯,但MVP更簡單。讓我們把它和MVC比較一下:
MVC模式更復雜。許多箭頭圍成一個圈。控制器的角色不明確,這種模式可以以許多不同的方式解釋。事實上,這是造成有太多該模式下客戶端框架的根本原因。
MVP則相反,沒有太多的解釋空間,歧義少。每部分的作用是明確的。它適合大大大小小的工程,是單元測試的最佳模式。
讓我們看一下MVP在Riot中是如何工作的。
Model
Riot的models定義了你的應用。 它是你的商業邏輯對外提供的一個深思熟慮后的API。一個完全隔離的、可在瀏覽器和服務器端(node.js)運行的可測試單元。下面是一個待辦事項應用。
function Todo(store) {var self = this, items = [];
self.add = function(label) { var item = { ... }; items.push(item);
// notify others self.emit("add", item);
}
self.remove = function(id) { delete items[id]; self.emit("remove", item); }
// + other public methods ...
// Enable MVP pattern (this is the secret for everything) $.observable(self);
// save state self.on("add remove edit", function() { store.put(items); })
}</pre>
這個Model是一個傳統的JavaScript對象(POJO),沒有框架風格。在MVC的術語中,這是一個域模型而不僅僅是數據訪問層。
你有權使用prototype對象或對象構造器{}。以上只是我個人使用的JavaScript風格。
當設計一個model時,保持思路明確很重要。你需要的最后一件事是用一個框架來聚焦你的應用邏輯。JavaScript本身就具有巨大的表現力。observable
觀察者是從你應用中分離模型的關鍵:
$.observable(object);當一些重要的事件發生后,上面的函數調用給定的對象并通知給其它對象。視圖將會重新繪制或者API使用者也可以用監聽改變事件來予以擴展。
觀察者是將你的應用分離為可維護的組件的關鍵。它是一個典型的設計模式來將模型從視圖中分離出來。
"構建大應用的秘籍就是永遠不要一下子做成大應用。要將你的應用分割為小的模塊"。Justin Meyer,JavaScriptMVC的作者
"如果將不同組件綁的太緊,可重用性將降低,而且很難在改變一些組件時而不影響其它的組件。"--Rebecca Murphey,jQuery Fundamentals作者在客戶端框架中一個好的事件庫是分離各組件的很重要的特性。這就是Riot受關注的原因。
觀察者可以對給定對象添加如下方法:
Riot 事件是基于jQuery事件的,所以它有很強大的特性,如命名空間,一次可以監聽多個事件。
最重要的不同是它沒有事件對象,因為它在DOM之外就沒有相關的事件了。你也能很優雅的發送和接收參數而不用數組來完成。
// 發送事件 obj.emit("my-event", "1st argument", "2nd arg", ... argN);// 接收事件 obj.on("my-event", function(arg1, arg2, ... argN) {
});</pre>
觀察者模式產生于1988年的Smlltalk語言,自從那時起它就被用來構建用戶接口了。它是一個設計模式,而不是框架。事件和監聽器的本質是被分離關注點的優良代碼。這也應該是你理念的核心部分。
換句話說:
你不需要用框架來寫一個模塊級的客戶端應用。
視圖
視圖就是你的應用中可見的那部分。譬如文本,圖片,表格,按鈕,鏈接等等,html代碼和css樣式文件。
Riot視圖盡可能的輕。沒有了那些視圖中本不該有的條件語句,循環,或者數據綁定,自定義的屬性或元素。就是你對所有網頁設計師期望的那種視圖。
只有"模版"——也就是html代碼塊能在運行的時候插到視圖里面。這些模版包含那些可以用Riot模版驅動很快用數據替換的變量。
下面是一段 待處理的MVC入口:
<li id="{id}"> <div class="view"> <input class="toggle" type="checkbox"> <label>{name}</label> <button class="destroy"/> </div> <input class="edit" value="{name}"> </li>這種”不合邏輯的html“ 沒有弱點或可測試的界面。 它通過了w3c校驗器并且運行更快。
實際邏輯在presenter里面.
呈現器
呈現器監聽視圖上的事件(點擊,滾動,按鍵,返回按鈕等等)以及模型(添加,刪除或修改的東西)上的事件,當模型更新后視圖和模型都會相應的變化。這種"中間人"顯示的定義了用戶是怎么使用接口的行為。下面是備忘錄應用:
$(function() {/ 1. 初始化 /
// 創建一個模型對象 var model = new Todo(),
// 抓出這個視圖HTML root實例 root = $("#todo-list"), // 單個備忘錄 HTML 模板 template = $("#todo-tmpl").html(),
/ 2. 監聽用戶事件 /
// 點擊"清除完畢" $("#clear-completed").click(function() { todo.remove("completed"); })
//點擊"切換所有" $("#toggle-all").click(function() { todo.toggle(filter); })
// ...
/ 3. 監聽模型事件 /
// 一個刪除入口 todo.on("remove", function(items) { $.each(items, function() { $(this.id).remove() })
// 一個編輯入口 }).on("edit", function(item) { var el = $(item.id); el.removeClass("editing"); $("label, .edit", el).text(item.name).val(item.name); })
// ...
})</pre>
在你的應用中能決定每一個不同呈現器的感覺非常自由。這是基于UI(邊欄,賬目,頭位置...)上的規則或是基于功能(登陸,添加,創建,刪除...)。
呈現器可以用jQuery來使用所有的用戶接口邏輯。
jQuery
jQuery絕對是最好的操作DOM的工具,而且它也能很好的創建呈現器。jQuery API設計的很漂亮而且所有的庫都非常有用。目前在56%的網站中都有它的身影,92%的網站中用的JavaScript庫都被人熟知。Riot將憑借它們引以為豪。
說用jQuery可以讓代碼更糟的錯誤觀點讓人失望。沒有什么可以超越真理。人們認為代碼很亂是因為代碼將模型和視圖混合了。為了防止此事件發生,通過觀察可以簡單的分離模型代碼和視圖代碼。你不需要框架的。
用jQuery構建MVP模式,你所需要做的就是用接口而已。
最糟糕的事情就是因為錯誤的原因而丟棄jQuery。它可是高水平的生產高雅和具有可讀性代碼的API。其表達式語法簡明而且只需要很少的鍵盤輸入。它擁有很大的社區、大量的文檔以及它能在所有瀏覽器上工作。用jQuery可以讓你感受到自由而不是害怕。
目前構建數據的框架是在HTML層次來減少混亂代碼的。突然間onclick屬性隱藏在后面了(我驚訝的看著你)。編程書這樣告訴我們,要將事物分離開來,在web開發中的意思如下:
Riot是簡潔的方法,它不會讓你在HTML視圖中參雜任何邏輯。
URL的返回按鈕
另一個讓人困惑的來源是返回按鈕。按照一般的約定,你需要一個叫“路由器”的工具。
其實這是不對的。
點擊返回按鈕或者改變URL是一個你可以監聽的全局窗口事件。它應該和按下一個鍵盤按鍵事件一樣。目前的路由器是一個為很基礎功能而產生的巨大解決方案。沒有必要將工具和處理URL事件相分離,這就像沒有必要將工具和處理按鍵事件分離一樣。
上面的路由器產生了混淆是因為它不符合典型的MVC模式。在之前的網絡時代,傳統的MVC應用沒有URL。
Riot只有一個$.route函數來處理應用的狀態和返回按鈕
// 點擊導航元素 $("#filters a").click(function(e) {// 跳過鏈接默認行為 e.preventDefault();
//改變URL哈希部分 $.route($(this).attr("href"));
})
//監聽URL改變事件 $.route(function(hash) { /* 待辦事件列表處理:
1. 清除列表并添加新項 2. 高亮導航上激活的元素 3. 更新視圖計數器
*/ })</pre>
你只需要一個函數。我把它稱作人們所期待的“路由”。它的作用如“url”或“go”。在url參數的基礎上,你可以有一個或著多個URL監聽器來完成其功能。
如果你的應用嚴重依靠不同哈希參數,你可能需要一個全面的路由器,但在大多數應用中是不需要的。
這就是Riot!
目前客戶端框架都存在以下錯誤的假設:
所有這些陳述都是相當的錯誤。
目前的框架都會勸說大家相信錯誤的信條,即在沒有易懂的、科學的分析下可以創建好的網站和非常精致的營銷。為了解決假設的問題而會產生新的問題。很不幸,大量的通信在處理不相干的問題點。
Riot就是反對現狀!
目前的應用可以更加的快、簡單和小巧。它們可以讓更少的人維護,需要更少的知識和出現更少的錯誤。
模型應該是普通的JavaScript,視圖應該是普通的HTML。它們兩都應該獨立的發展而互不影響。框架沒有必要負擔開發和命令事情如何發展。
沒有“Backbone的方式”、“Angular 方式”或是“Ember的方式”。框架一直都是經典的編程技巧。
Riot是對平凡的JavaScript和jQuery的一種宣言。
接下來呢?
我 之前的一個博客主題是有關Riot.js和無框架化的JavaScript。我想要用無框架結構來控制應用。事實上,只需要3個函數即可。
我的下一篇博客主題則更深層次的討論了用Riot構建現代化的JavaScript應用。你可以學習到Riot怎么處理測試,持久化,應用輔助程序,資源分享,以及文件操作。
最后,官方的Riot.js即將出發行版,并且它有為瀏覽器和nod.js發布了文檔和Moot論壇。關注我們的 非死book頁面和 推ter公告。
瀏覽源碼
Riot.js
Riot的MVC備忘錄
模板性能測試