構建單頁面應用


所謂單頁應用,指的是在一個頁面上集成多種功能,甚至整個系統就只有一個頁面,所有的業務功能都是它的子模塊,通過特定的方式掛接到主界面上。它是AJAX技術的進一步升華,把AJAX的無刷新機制發揮到極致,因此能造就與桌面程序媲美的流暢用戶體驗。

開發框架
ExtJS可以稱為第一代單頁應用框架的典型,它封裝了各種UI組件,用戶主要使用JavaScript來完成整個前端部分,甚至包括布局。隨着功能逐漸增加,ExtJS的體積也逐漸增大,即使用於內部系統的開發,有時候也顯得笨重了,更不用說開發以上這類運行在互聯網上的系統。
jQuery由於偏重DOM操作,它的插件體系又比較松散,所以比ExtJS這個體系更適合開發在公網運行的單頁系統,整個解決方案會相對比較輕量、靈活。
但由於jQuery主要面向上層操作,它對代碼的組織是缺乏約束的。如何在代碼急劇膨脹的情況下控制每個模塊的內聚性,並且適當在模塊之間產生數據傳遞與共享,就成為了一種有挑戰的事情。

為了解決單頁應用規模增大時候的代碼邏輯問題,出現了不少MV*框架,他們的基本思路都是在JS層創建模塊分層和通信機制。有的是MVC,有的是MVP,有的是MVVM,而且,它們幾乎都在這些模式上產生了變異,以適應前端開發的特點。

這類框架包括Backbone,Knockout,AngularJS,Avalon等。

組件化
這些在前端做分層的框架推動了代碼的組件化,所謂組件化,在傳統的Web產品中,更多的指UI組件,但其實組件是一個廣泛概念,傳統Web產品中UI組件占比高的原因是它的厚度不足,隨着客戶端代碼比例的增加,相當一部分的業務邏輯也前端化,由此催生了很多非界面型組件的出現。
分層帶來的一個優勢是,每層的職責更專一了,由此,可以對其作單元測試的覆蓋,以保證其質量。傳統UI層測試最頭疼的問題是UI層和邏輯混雜在一起,比如往往會在遠程請求的回調中更改DOM,當引入分層之后,這些東西都可以分別被測試,然后再通過場景測試來保證整體流程。

代碼隔離
與開發傳統頁面型網站相比,實現單頁應用的過程中,有一些比較值得特別關注的點。

從單頁應用的特點來看,它比頁面型網站更加依賴於JavaScript,而由於頁面的單頁化,各種子功能的JavaScript代碼聚集到了同一個作用域,所以代碼的隔離、模塊化變得很重要。

在單頁應用中,頁面模板的使用是很普遍的。很多框架內置了特定的模板,也有的框架需要引入第三方的模板。這種模板是界面片段,我們可以把它們類比成JavaScript模塊,它們是另一種類型的組件。

模板也一樣有隔離的需要。不隔離模板,會造成什么問題呢?模板間的沖突主要存在於id屬性上,如果一個模板中包含固定的id,當它被批量渲染的時候,會造成同一個頁面的作用域中出現多個相同id的元素,產生不可預測的后果。因此,我們需要在模板中避免使用id,如果有對DOM的訪問需求,應當通過其他選擇器來完成。如果一個單頁應用的組件化程度非常高,很可能整個應用中都沒有元素id的使用。

代碼合並與加載策略

人們對於單頁系統的加載時間容忍度與Web頁面不同,如果說他們願意為購物頁面的加載等待3秒,有可能會願意為單頁應用的首次加載等待5-10秒,但在此之后,各種功能的使用應當都比較流暢,所有子功能頁面盡量要在1-2秒時間內切換成功,否則他們就會感覺這個系統很慢。

從這些特點來看,我們可以把更多的公共功能放到首次加載,以減小每次加載的載入量,有一些站點甚至把所有的界面和邏輯全部放到首頁加載,每次業務界面切換的時候,只產生數據請求,因此它的響應是非常迅速的,比如青雲的控制台就是這么做的。

通常在單頁應用中,無需像網站型產品一樣,為了防止文件加載阻塞渲染,把js放到html后面加載,因為它的界面基本都是動態生成的。

當切換功能的時候,除了產生數據請求,還需要渲染界面,這個新渲染的界面部件一般是界面模板,它從哪里來呢?來源無非是兩種,一種是即時請求,像請求數據那樣通過AJAX獲取過來,另一種是內置於主界面的某些位置,比如script標簽或者不可見的textarea中,后者在切換功能的時候速度有優勢,但是加重了主頁面的負擔。

在傳統的頁面型網站中,頁面之間是互相隔離的,因此,如果在頁面間存在可復用的代碼,一般是提取成單獨的文件,並且可能會需要按照每個頁面的需求去進行合並。單頁應用中,如果總的代碼量不大,可以整體打包一次在首頁載入,如果大到一定規模,再作運行時加載,加載的粒度可以搞得比較大,不同的塊之間沒有重復部分。

路由與狀態的管理

管理路由的目的是什么呢?是為了能減少用戶的導航成本。比如說我們有一個功能,經歷過多次導航菜單的點擊,才呈現出來。如果用戶想要把這個功能地址分享給別人,他怎么才能做到呢?

傳統的頁面型產品是不存在這個問題的,因為它就是以頁面為單位的,也有的時候,服務端路由處理了這一切。但是在單頁應用中,這成為了問題,因為我們只有一個頁面,界面上的各種功能區塊是動態生成的。所以我們要通過對路由的管理,來實現這樣的功能。

具體的做法就是把產品功能划分為若干狀態,每個狀態映射到相應的路由,然后通過pushState這樣的機制,動態解析路由,使之與功能界面匹配。

有了路由之后,我們的單頁面產品就可以前進后退,就像是在不同頁面之間一樣。

其實在Web產品之外,早就有了管理路由的技術方案,Adobe Flex中,就會把比如TabNavigator,甚至下拉框的選中狀態對應到url上,因為它也是單“頁面”的產品模式,需要面對同樣的問題。

緩存與本地存儲
在單頁應用的運作機制中,緩存是一個很重要的環節。
由於這類系統的前端部分幾乎全是靜態文件,所以它能夠有機會利用瀏覽器的緩存機制,而比如動態加載的界面模板,也完全可以做一些自定義的緩存機制,在非首次的請求中直接取緩存的版本,以加快加載速度。
甚至,也出現了一些方案,在動態加載JavaScript代碼的同時,把它們也緩存起來。比如Addy Osmani的這個basket.js,就利用了HTML5 localStorage作了js和css文件的緩存。

在單頁產品中,業務代碼也常常會需要跟本地存儲打交道,存儲一些臨時數據,可以使用localStorage或者localStorageDB來簡化自己的業務代碼。

服務端通信
傳統的Web產品通常使用JSONP或者AJAX這樣的方式與服務端通信,但在單頁Web應用中,有很大一部分采用WebSocket這樣的實時通訊方式。
WebSocket與傳統基於HTTP的通信機制相比,有很大的優勢。它可以讓服務端很便利地使用反向推送,前端只響應確實產生業務數據的事件,減少一遍又一遍無意義的AJAX輪詢。

由於WebSocket只在比較先進的瀏覽器上被支持,有一些庫提供了在不同瀏覽器中的兼容方案,比如http://socket.io,它在不支持WebSocket的瀏覽器上會降級成使用AJAX或JSONP等方式,對業務代碼完全透明、兼容。

內存管理
傳統的Web頁面一般是不需要考慮內存的管理的,因為用戶的停留時間相對少,即使出現內存泄漏,可能很快就被刷新頁面之類的操作沖掉了,但單頁應用是不同的,它的用戶很可能會把它開一整天,因此,我們需要對其中的DOM操作、網絡連接等部分格外小心。

樣式的規划
樣式規划主要是幾個方面:
基准樣式的分離

這里面主要包括瀏覽器樣式的重設、全局字體的設置、布局的基本約定和響應式支持。

組件樣式的划分
這里面是兩個層面的規划,首先是各種界面組件及其子元素的樣式,其次是一些修飾樣式。組件樣式應當盡量減少互相依賴,各組件的樣式允許冗余。

堆疊次序的管理
傳統Web頁面的特點是元素多,但是層次少,單頁應用會有些不同。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM