李占倉
(天津職業大學,天津 300410)
在Web 應用開發領域,目前有兩種主流的構建方式,一種是采用多頁面的傳統多頁應用,另一種是采用單頁面的單頁應用[1]。多頁應用一般由多個頁面組成,頁面間的跳轉往往需要后端路由的支持,每次的頁面跳轉,都需要向服務端重新請求整個新的頁面;單頁應用只有一個頁面,視圖的切換由前端路由負責,本質上只是不同的DOM 元素的顯示與隱藏,不需要再次向服務器請求新的完整頁面,可以通過AJAX向服務端異步請求數據,并渲染到指定的DOM元素[2]。本文從實現原理,實現方法兩方面,對兩種應用進行了全面的對比。特別是對于單頁Web 應用,詳細分析了其實現原理,并介紹了具體的實現方式,為Web應用開發者提供了參考。
單頁應用一般是指一種單頁的Web 應用,是相對于傳統的多頁應用而說的,可以通過與多頁應用對比,理解單頁應用的概念。
多頁應用(Mutiple Page Application),簡稱MPA,是指一個Web 應用由多個完整的Html 頁面組成,每個Html 頁面都有自己的CSS 和JavaScript文件。在多個頁面間進行切換時,通常依賴于服務端的路由技術,或者使用超連接標簽的href 屬性,或者通過JavaScript 控制location 對象的href 屬性,向服務器請求整個Html文件。
由于MPA 應用在使用的過程中,需要不斷的頁面切換,進而導致不斷的重新請求頁面,瀏覽器需要不停的創建完整的DOM 樹,刪除完整的DOM 樹,不但造成不必要的網絡傳輸,而且增加了客戶端的負擔,嚴重影響了用戶的體驗[3]。多頁Web應用示意圖,如圖1所示:

圖1 多頁應用示意圖
在圖1中,Web應用由多個頁面,在瀏覽器端觸發的每次頁面跳轉操作,都會請求一個完整的Html頁面,并在瀏覽器對整個頁面進行渲染。
單頁應用(Single Page Web Application),簡稱SPA,是指整個Web 應用只有一個html 頁面組成,該頁面在初始化時一次性加載相應的Html、JavaScript和Css,一旦加載完成,在瀏覽器運行期間不再重新加載,之后所有的交互操作都在這一個頁面上完成。其原理是借助JavaScript 響應用戶的操作,動態的隱藏或顯示相應的內容,并通過Ajax 請求服務端的數據,使用JavaScript 對數據進行頁面渲染。
由于單頁應用避免了頁面的重新加載,最大程度的減少了不必要的網絡傳輸,并且瀏覽器不需要重新創建和銷毀完整的DOM 樹,大大減輕了客戶端的負擔,因此SPA 可以提供較為流暢的用戶體驗[4]。單頁應用示意圖,如圖2所示:

圖2 單頁應用示意圖
在圖2中,Web應用只有一個單頁面組成,在瀏覽器首次訪問應用時,一次性加載單頁以及單頁包含的所有內容,包括CSS、JavaScript、圖片等。單頁面中往往包含多個模塊,所謂的視圖切換,就是使用JavaScript控制多個DOM模塊的顯示與隱藏。圖2 中的“當前模塊”為當前顯示的模塊,其他模塊為隱藏的模塊。之后的視圖切換操作不會再次請求服務端的頁面。可以以AJAX的方式異步請求服務端的數據,并渲染到單頁面的不同的模塊中。
單頁應用和多頁應用在實現原理上,最大的區別體現在采用的路由機制不同,單頁應用采用前端路由,多頁應用一般采用后端路由。
從功能上解釋,Web 應用中的路由是指URL與瀏覽器顯示的內容之間的映射,即隨著URL 的變化,頁面中顯示不同的內容;從原理上解釋,路由是指URL 到處理函數之間的映射,即由不同的函數來處理不同的URL,從而實現界面的切換。
后端路由也稱服務器端路由[5],由服務器來實現URL 與處理函數之間的映射。實現過程是:當服務器接收到客戶端的HTTP請求時,會根據請求對象中的URL,找到服務器端對應的處理函數并執行,然后將函數的返回值響應給客戶端。在后端路由中,根據URL 請求的內容不同,處理函數主要有三種不同的處理方式:
1.對于靜態資源的請求,函數的功能就是對服務端靜態資源的讀取,如html 頁面、圖片等,將這些靜態資源直接返回,不進行加工處理。
2.對于動態資源的請求,函數的功能就是首先執行動態資源中的腳本,其中可能包括讀取數據庫,處理相應的業務,再使用相應的模板將數據渲染到頁面,生成對應的靜態頁面并返回。
3.對于數據的請求,函數的功能就是讀取數據庫,將數據以某種格式響應給客戶端,如JSON、XML等,本質上數據也是一種動態資源。
后端路由應用項目有如下特點:靜態資源和動態資源在一個項目中,部署在一起;所有的頁面請求都由服務器端處理,然后響應給客戶端,優缺點如下:
優點:安全性能好、SEO 表現好、首屏時間(瀏覽器顯示第一屏頁面所消耗的時間)短,前端處理壓力小等。
缺點:服務器端壓力大,頻繁的頁面請求浪費不必要的網絡資源,客戶端渲染任務重,用戶體驗差等。
前端路由就是由客戶端來實現URL 與處理函數之間的映射,處理函數由JavaScript 實現,處理方式就是進行一些DOM的顯示和隱藏[6]。在處理路由的過程中,不再向服務端請求頁面,極大的提升了用戶的體驗。隨著前后端分離式開發的流行和單頁應用的普及,前端路由得到了廣泛的應用。前端路由Web 應用項目有如下特點:應用的前后端的開發可以徹底分開,一個Web 項目可以由一個前端項目和一個后端項目組成,后端項目只為前端項目提供API接口,其余的工作均由前端項目完成,部署時,前后端也可以分開部署。前端路由優缺點如下:
優點:路由速度快,用戶體驗好,項目前后端耦合度小,分工更明確,開發更專業。
缺點:首屏時間長;SEO表現差,不利于搜索引擎收錄;容易造成CSS命名沖突;使用瀏覽器的前進,后退鍵時,會重新發送請求,重新通過AJAX獲取數據,不能合理的利用緩存等。
從實現原理上講,單頁應用有兩種實現方式,一種是基于hash 的前端路由,另一種是基于H5新增的history API的前端路由。
Hash是BOM中location對象的一個屬性,它表示URL 的錨部分,比如URL 為:http://localhost/index.html#student,則location.hash 的 值 為“#student”,hash值從#號開始[7]。
通過Hash實現前端路由思路如下:
1.創建路由按鈕和初始視圖DOM。
2.通過點擊路由按鈕改變URL中的Hash值。
3.當Hash 發生變化時,觸發window 對象的onhashchange事件。
明確中國有構建國際商事法庭的必要性之后,在設計國際商事法庭的具體規則之前,我們需要考慮的是中國國際商事法庭的定位問題,或者說是中國國際商事法庭的管轄標準問題。該問題直接決定了中國國際商事法庭如何建設。
4.在onhashchange 事件處理函數中實現DOM的更新。
具體代碼如下:


代碼執行步驟和說明如下:
1.創建超連接表示路由按鈕,創建id為view的div表示初始視圖。
2.定義一個Router類,成員如下:
routes 對象屬性:表示路由集合,一個路由由一個路由標識和一個與之對應的處理方法組成,屬性名表示路由標識,屬性值表示對應的處理方法。
currentUrl字符串屬性:表示當前的路由標識。
route 方法:用于構造routes,向routes 集合中添加路由。
refresh 方法:hashchange 事件的響應函數,會根據不同的hash,調用不同的處理方法。
Init 方法:用于初始化Router 對象,為window對象的load事件和hashchange事件綁定響應方法。
3.實例化Router 對象router 并調用init 方法,完成初始化,為window 的load 事件和hashchange事件綁定處理方法refresh。
4.調用router對象的route方法向路由集合中添加兩個路由。
5.當點擊超路由按鈕時,由于改變了url 中的hash值,refresh方法被執行。
6.refresh 方法獲取當前的hash 值,去掉#之后轉換為路由標識,再調用與路由標識對應的處理方法,更新DOM。
history 對象是window 對象的一部分,它包含用戶訪問過的URL。在H5中,history 添加了push-State()和replaceState()兩個方法,可以向歷史棧中添加記錄,從而模擬瀏覽歷史和前進后退,實現前端路由[8]。
History實現前端路由思路如下:
1.創建路由按鈕和初始視圖DOM。
2.為路由按鈕綁定處理方法。
3.當點擊路由按鈕時,觸發處理方法,通過pushState 向歷史棧中添加URL 記錄,同時傳遞路由標識參數信息,并更新視圖。
4.當執行前進后退操作時,觸發window 的popstate事件,在popstate事件的處理函數中,接收popstate 事件參數,從中取出路由標識,再根據路由標識,更新視圖實現代碼如下。


代碼執行步驟和說明如下:
1.定義一個Router類,成員如下:
routes 對象屬性:表示路由集合,屬性名表示路由標識,屬性值表示對應的處理方法。
route 方法:用于構造routes,向routes 集合中添加路由。
refresh 方法:popstate 事件的響應函數,從參數中獲取路由標識,根據路由標識,調用不同的處理方法更新視圖。
Init方法:用于Router對象的初始化。
2.實例化Router對象router。
3.調用router 的init 方法,完成初始化,內容包括:為路由按鈕添加點擊事件處理方法,為window對象的hashchange事件綁定響應方法。
4.調用router對象的route方法向路由集合中添加兩個路由。
5.當點擊路由按鈕時,執行路由按鈕的響應方法,從路由按鈕中獲取路由標識,通過history 的pushState 向歷史棧中添加記錄,同時傳遞路由標識參數{name:link},然后調用與路由標識對應的路由處理方法,更新視圖。
6.當點擊瀏覽器的前進后退按鈕時,觸發hashchange 事件處理方法,從參數中獲取路由標識,根據路由標識,調用對應的處理方法,更新視圖。
單頁應用以其近似原生應用的用戶體驗、前后端分離開發方式等優勢,在Web 應用開發領域愈發流行。當前流行的三大MVVM 框架Vue.js,React,angularJS,都支持單頁應用開發,實現單頁應用的核心技術在于路由的處理,三大框架都提供了完備的路由機制。對于開發者而言,在沒有充分理解前端路由原理的情況下,直接使用框架提供的路由方案進行開發,往往知其然而不知其所以然,在出現問題時,也不容易排錯。文章通過與多頁應用的對比,詳細介紹了單頁應用的原理,通過與后端路由對比,介紹了前端路由的實現原理,最后,通過Hash 和Location 兩種技術,從低層對單頁應用進行了實現。為開發者提供了技術參考,在理解原理的情況,使用框架技術進行前端開發,會顯得更加得心應手。