ASP.NET MVC
如果你還沒有接觸過後端的MVC框架的話,不妨先看看下面這段ASP.NET MVC代碼並且了解一下後端MVC的工 作原理。它摘自ASP.NET MVC教程中非常著名的項目MVC Music Store一段Controller組件代碼:
public class StoreManagerController : Controller { private MusicStoreEntities db = new MusicStoreEntities(); // GET: /StoreManager/ public ViewResult Index() { var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist); return View(albums.ToList()); } // GET: /StoreManager/Details/5 public ViewResult Details(int id) { Album album = db.Albums.Find(id); return View(album); } }
我們知道Controller的職責之一是負責響應用戶在視圖上的行為,而具體每個行為應如何進行響應,需要 落實到Controller具體的方法上,這個方法我們可以稱之為action。上面代碼中的兩個公開方法Index()與 Details()就是兩個action。它們都屬於StoreManager這個Controller。如果你有使用過前端的Ember.js的話 ,應該對這兩個概念非常熟悉。
但問題來了,如何將用戶在視圖上的行為,與響應行為的方法action關聯起來?甚至與Controller關聯起 來? URL便是方法之一。上面代碼每個action上的注釋便代表這個這個action對應的URL。也就是說,當用戶 點擊該URL時,框架中的Router服務便能通過URL解析出應該調用哪個Controller及該Controller下的哪一個 action進行響應,以上面的例子為例,可以知道URL的規則為{controller}/{action}/{id}。
那麼響應的結果應該是什麼呢?從上面的代碼ViewResult和return View()兩處可以看出,兩個action返回 的都是新的視圖。
舉一個最熟悉的現有MVC站點的例子便是github。你會發現你在github網站上的每一處點擊都有唯一的URL 對應,每一次交互的結果都是服務器返回新的頁面。它使用javascript非常少,比如當你選擇編輯時,它也會 跳轉到一個新的頁面,而非在當前頁彈出一個編輯框。
為什麼首先要聊這麼多的服務器端MVC框架的特性。因為接下來回過頭來看前端的MVC框架時,你會發現有 非常多的差異之處。
Javascript MVC
從上面可以看出,服務器端的MVC框架服務的是整個站點,它依靠不斷的返回頁面來響應用戶請求,因此 Router服務至關重要。而使用MVC框架的前端頁面,大多數是Single Page Application,甚至還不如單頁面, 只是頁面上的某一個組件,比如一個Slide。因此將用戶的行為轉化為URL是不現實。你或許會說的確無法生產 新的頁面,那麼降低頁面粒度如何呢?也就是說在服務器端一個URL映射的是一個頁面,那麼我們將URL映射為 頁面的某個區域或者功能呢?
比如以下面這段Backbone.js的TodoList應用Router為例:
var TodoRouter = Backbone.Router.extend({ routes: { 'todo/add': 'add', // 新增項 'todo/edit/:id': 'edit', // 編輯項 'todo/remove/:id': 'remove', // 刪除項 'filter/completed': 'filterCompleted', // 過濾出已完成 'filter/uncompleted': 'filterUncompleted' // 過濾出未完成 } // Todo });
如果依照這樣Route規劃,我們希望當用戶輸入http://example.com#todo/add時,我們彈出的是一個新增 輸入框;而當用戶輸入http://example.com#todo/edit/123456頁面出現編輯id為123456的這條記事的編輯框 。這樣我們便將URL映射的頁面粒度降低為輸入框粒度。
但是這樣會引起另一個問題,注意上面route的差別:todo/域名下操作的是單條的記錄,而filter/域名下 操作的是對列表進行篩選。所以還不得不考慮一種情況,如果用戶想在篩選的情況下可否對每一項進行操作? 如果允許的話,參考排列組合,route是否需要新增為2 x 3 = 6項?如新增 http://example.com#filter/completed/todo/add這樣的路由。
這樣的設定明顯是不合理的。之所以會產生這樣的問題是因為對後端而言URL與頁面是一一對應的關系。而 如果降低頁面粒度的話,無法將頁面功能與URL對應起來,或者說如果想讓URl覆蓋單一頁面上的所有功能的成 本太高了。