在瀏覽器端,對路由的理解通常是根據不一樣的 URL 完成頁面的切換。在服務器端,則是根據不一樣的 URL 請求回饋相關的頁面。在本章,咱們廣義的組件路由的定義:根據接收到的不一樣命令,組件對象呈現出不一樣的子級頁面。在這裏將介紹與路由相關的一個組件,即視圖棧 ViewStack。html
該組件在《文檔》部分的最後一個章節《延遲實例化》已經出現過了。這裏將對一些細節部分進行解讀。下面再次給出該組件的源碼。html5
// 07-01 ViewStack: { xml: "<div id='viewstack'/>", fun: function (sys, items, opts) { var args, children = this.children(), table = children.call("hide").hash(), ptr = table[opts.index] || children[0]; if (ptr) ptr = ptr.trigger("show").show(); this.on("switch", function ( e, to ) { table = this.children().hash(); if ( !table[to] || table[to] == ptr ) return; e.stopPropagation(); args = [].slice.call(arguments).slice(2); ptr.trigger("hide", [to+''].concat(args)).hide(); ptr = table[to].trigger("show", [ptr+''].concat(args)).show(); }); return Object.defineProperty({}, "selected", { get: function() {return ptr;}}); } }
從靜態接口看,該組件容許提供靜態參數 index
,該參數是組件 ViewStack 某一兒子組件對象的名稱,它用於指出哪個子級組件會被最早呈現。請看下面的示例。api
// 07-01 Index: { xml: `<ViewStack index='bar'> <button id='foo'>foo</button> <button id='bar'>bar</button> </ViewStack>` }
該示例中,ViewStack 包含一值爲 bar
的屬性 index
,代表組件在實例化時,組件對象 bar 會最早呈現。而默認狀況下,該組件的第一個子級組件會做爲初始顯示對象。再從動態接口看,該組件的函數項導出了一個名爲 selected
的只讀屬性,該屬性用於指示當前顯示的子級組件對象。瀏覽器
對於子級組件對象之間切換,該組件的函數項並未導出相關的接口,而是經過接收 switch
事件來完成切換。請看下面的示例。服務器
// 07-02 Index: { xml: `<ViewStack id='index'> <button id='foo'>foo</button> <button id='bar'>bar</button> </ViewStack>`, fun: function (sys, items, opts) { sys.index.on("click", "*", function(e) { var to = this + '' == "foo" ? "bar" : "foo", data = "hello, world"; this.trigger("switch", [to, data]); }); sys.foo.on("show", function (e, prev, data) { console.log("previous page is " + prev, "from data is " + data); }); sys.bar.on("hide", function (e, prev, data) { console.log("previous page is " + prev, "from data is " + data); }); } }
對於該示例,當用戶點擊文字時,文字會在 foo 和 bar 之間切換,也即兩個頁面之間切換,切換是經過相應子級對象派發 switch
事件進行的。另外,組件 ViewStack 在切換頁面時,還會對本次顯示的頁面派發事件 show
,以及對本次隱藏的頁面派發事件 hide
,相關頁面能夠根據須要選擇偵聽與否。而且在偵聽函數中,能夠獲知前一顯示頁面 ID
以及所傳輸的相關數據。app
組件 ViewStack 支持動態添加與移除子級的組件對象,請看下面的一個示例。ide
// 07-03 Index: { xml: `<ViewStack id='index'> <button id='foo'>foo</button> </ViewStack>`, fun: function (sys, items, opts) { sys.foo.on("click", function () { var xml = "<button id='bar'>bar</button>"; sys.index.append(xml).trigger("switch", "bar"); }); } }
該示例中,當用戶點擊按鈕 foo 應用會動態添加了一個子級組件,而且經過派發事件 switch
將當前顯示的視圖切換爲剛新添加的視圖。函數
組件 ViewStack 通常配合組件的延遲實例化功能使用。對於一些比較複雜的組件,這樣有助於加快顯示應用的初始頁面。下面作簡單示範。優化
// 07-04 Index: { xml: `<ViewStack id='index'> <button id='foo'>foo</button> <button id='bar'>bar</button> </ViewStack>`, map: { defer: "bar" }, fun: function (sys, items, opts) { sys.foo.on("click", function () { sys.index.trigger("switch", "bar"); }); } }
此示例中,ViewStack 子級包含三個子組件,其中組件對象 bar 被設置爲須要延遲實例化,只有當視圖切換在組件對象 bar 時,它才真正開始實例化。this
這裏咱們看看如何讓組件 ViewStack 與 HTML5 History API 的配合使用。下面是一個簡單的例子。
// 07-05 Index: { xml: "<ViewStack id='index'>\ <button id='foo'>foo</button>\ <button id='bar'>bar</button>\ </ViewStack>", fun: function (sys, items, opts) { sys.index.on("show", "button", function (e) { window.history.pushState({name: this + ""}, null, "/" + this); }); window.addEventListener("popstate", function (e) { e.state && sys.index.trigger("switch", e.state.name); }); sys.foo.on("click", e => sys.foo.trigger("switch", "bar")); sys.bar.on("click", e => sys.foo.trigger("switch", "foo")); } }
該示例的關鍵點在於,當視圖棧組件對象的子級頁面發生變動時,使用函數 pushState 記錄下來;另外須要偵聽瀏覽器的 popstate 事件,當用戶點擊「前進」、「後退」按鈕時,完成相應頁面的切換。這種技術很是適合在單頁應用中完成無刷新跳轉,能夠給用戶帶來很是好的體驗。