如何構(gòu)建單頁Web應(yīng)用?
時間:2023-05-28 10:30:01 | 來源:網(wǎng)站運營
時間:2023-05-28 10:30:01 來源:網(wǎng)站運營
如何構(gòu)建單頁Web應(yīng)用?:
單頁應(yīng)用是什么?讓我們先來看幾個網(wǎng)站:
coding(
https://coding.net/)
teambition(
https://www.teambition.com/)
cloud9(
https://c9.io/)
注意這幾個網(wǎng)站的相同點,那就是在瀏覽器中,做了原先“應(yīng)當”在客戶端做的事情。它們的界面切換非常流暢,響應(yīng)很迅速,跟傳統(tǒng)的網(wǎng)頁明顯不一樣,它們是什么呢?這就是單頁Web應(yīng)用。
所謂單頁應(yīng)用,指的是在一個頁面上集成多種功能,甚至整個系統(tǒng)就只有一個頁面,所有的業(yè)務(wù)功能都是它的子模塊,通過特定的方式掛接到主界面上。它是AJAX技術(shù)的進一步升華,把AJAX的無刷新機制發(fā)揮到極致,因此能造就與桌面程序媲美的流暢用戶體驗。
其實單頁應(yīng)用我們并不陌生,很多人寫過ExtJS的項目,用它實現(xiàn)的系統(tǒng),很天然的就已經(jīng)是單頁的了,也有人用jQuery或者其他框架實現(xiàn)過類似的東西。用各種JS框架,甚至不用框架,都是可以實現(xiàn)單頁應(yīng)用的,它只是一種理念。有些框架適用于開發(fā)這種系統(tǒng),如果使用它們,可以得到很多便利。
開發(fā)框架ExtJS可以稱為第一代單頁應(yīng)用框架的典型,它封裝了各種UI組件,用戶主要使用JavaScript來完成整個前端部分,甚至包括布局。隨著功能逐漸增加,ExtJS的體積也逐漸增大,即使用于內(nèi)部系統(tǒng)的開發(fā),有時候也顯得笨重了,更不用說開發(fā)以上這類運行在互聯(lián)網(wǎng)上的系統(tǒng)。
jQuery由于偏重DOM操作,它的插件體系又比較松散,所以比ExtJS這個體系更適合開發(fā)在公網(wǎng)運行的單頁系統(tǒng),整個解決方案會相對比較輕量、靈活。
但由于jQuery主要面向上層操作,它對代碼的組織是缺乏約束的。如何在代碼急劇膨脹的情況下控制每個模塊的內(nèi)聚性,并且適當在模塊之間產(chǎn)生數(shù)據(jù)傳遞與共享,就成為了一種有挑戰(zhàn)的事情。
為了解決單頁應(yīng)用規(guī)模增大時候的代碼邏輯問題,出現(xiàn)了不少MV*框架,他們的基本思路都是在JS層創(chuàng)建模塊分層和通信機制。有的是MVC,有的是MVP,有的是MVVM,而且,它們幾乎都在這些模式上產(chǎn)生了變異,以適應(yīng)前端開發(fā)的特點。
這類框架包括Backbone,Knockout,AngularJS,Avalon等。
組件化這些在前端做分層的框架推動了代碼的組件化,所謂組件化,在傳統(tǒng)的Web產(chǎn)品中,更多的指UI組件,但其實組件是一個廣泛概念,傳統(tǒng)Web產(chǎn)品中UI組件占比高的原因是它的厚度不足,隨著客戶端代碼比例的增加,相當一部分的業(yè)務(wù)邏輯也前端化,由此催生了很多非界面型組件的出現(xiàn)。
分層帶來的一個優(yōu)勢是,每層的職責更專一了,由此,可以對其作單元測試的覆蓋,以保證其質(zhì)量。傳統(tǒng)UI層測試最頭疼的問題是UI層和邏輯混雜在一起,比如往往會在遠程請求的回調(diào)中更改DOM,當引入分層之后,這些東西都可以分別被測試,然后再通過場景測試來保證整體流程。
代碼隔離與開發(fā)傳統(tǒng)頁面型網(wǎng)站相比,實現(xiàn)單頁應(yīng)用的過程中,有一些比較值得特別關(guān)注的點。
從單頁應(yīng)用的特點來看,它比頁面型網(wǎng)站更加依賴于JavaScript,而由于頁面的單頁化,各種子功能的JavaScript代碼聚集到了同一個作用域,所以代碼的隔離、模塊化變得很重要。
在單頁應(yīng)用中,頁面模板的使用是很普遍的。很多框架內(nèi)置了特定的模板,也有的框架需要引入第三方的模板。這種模板是界面片段,我們可以把它們類比成JavaScript模塊,它們是另一種類型的組件。
模板也一樣有隔離的需要。不隔離模板,會造成什么問題呢?模板間的沖突主要存在于id屬性上,如果一個模板中包含固定的id,當它被批量渲染的時候,會造成同一個頁面的作用域中出現(xiàn)多個相同id的元素,產(chǎn)生不可預(yù)測的后果。因此,我們需要在模板中避免使用id,如果有對DOM的訪問需求,應(yīng)當通過其他選擇器來完成。如果一個單頁應(yīng)用的組件化程度非常高,很可能整個應(yīng)用中都沒有元素id的使用。
代碼合并與加載策略人們對于單頁系統(tǒng)的加載時間容忍度與Web頁面不同,如果說他們愿意為購物頁面的加載等待3秒,有可能會愿意為單頁應(yīng)用的首次加載等待5-10秒,但在此之后,各種功能的使用應(yīng)當都比較流暢,所有子功能頁面盡量要在1-2秒時間內(nèi)切換成功,否則他們就會感覺這個系統(tǒng)很慢。
從這些特點來看,我們可以把更多的公共功能放到首次加載,以減小每次加載的載入量,有一些站點甚至把所有的界面和邏輯全部放到首頁加載,每次業(yè)務(wù)界面切換的時候,只產(chǎn)生數(shù)據(jù)請求,因此它的響應(yīng)是非常迅速的,比如青云的控制臺就是這么做的。
通常在單頁應(yīng)用中,無需像網(wǎng)站型產(chǎn)品一樣,為了防止文件加載阻塞渲染,把js放到html后面加載,因為它的界面基本都是動態(tài)生成的。
當切換功能的時候,除了產(chǎn)生數(shù)據(jù)請求,還需要渲染界面,這個新渲染的界面部件一般是界面模板,它從哪里來呢?來源無非是兩種,一種是即時請求,像請求數(shù)據(jù)那樣通過AJAX獲取過來,另一種是內(nèi)置于主界面的某些位置,比如script標簽或者不可見的textarea中,后者在切換功能的時候速度有優(yōu)勢,但是加重了主頁面的負擔。
在傳統(tǒng)的頁面型網(wǎng)站中,頁面之間是互相隔離的,因此,如果在頁面間存在可復(fù)用的代碼,一般是提取成單獨的文件,并且可能會需要按照每個頁面的需求去進行合并。
單頁應(yīng)用中,如果總的代碼量不大,可以整體打包一次在首頁載入,如果大到一定規(guī)模,再作運行時加載,加載的粒度可以搞得比較大,不同的塊之間沒有重復(fù)部分。
路由與狀態(tài)的管理我們最開始看到的幾個在線應(yīng)用,有的是對路由作了管理的,有的沒有。
管理路由的目的是什么呢?是為了能減少用戶的導(dǎo)航成本。比如說我們有一個功能,經(jīng)歷過多次導(dǎo)航菜單的點擊,才呈現(xiàn)出來。
如果用戶想要把這個功能地址分享給別人,他怎么才能做到呢?
傳統(tǒng)的頁面型產(chǎn)品是不存在這個問題的,因為它就是以頁面為單位的,也有的時候,服務(wù)端路由處理了這一切。
但是在單頁應(yīng)用中,這成為了問題,因為我們只有一個頁面,界面上的各種功能區(qū)塊是動態(tài)生成的。所以我們要通過對路由的管理,來實現(xiàn)這樣的功能。
具體的做法就是把產(chǎn)品功能劃分為若干狀態(tài),每個狀態(tài)映射到相應(yīng)的路由,然后通過pushState這樣的機制,動態(tài)解析路由,使之與功能界面匹配。
有了路由之后,我們的單頁面產(chǎn)品就可以前進后退,就像是在不同頁面之間一樣。
其實在Web產(chǎn)品之外,早就有了管理路由的技術(shù)方案,Adobe Flex中,就會把比如TabNavigator,甚至下拉框的選中狀態(tài)對應(yīng)到url上,因為它也是單“頁面”的產(chǎn)品模式,需要面對同樣的問題。
當產(chǎn)品狀態(tài)復(fù)雜到一定程度的時候,路由又變得很難應(yīng)用了,因為狀態(tài)的管理極其麻煩,比如開始的時候我們演示的
http://c9.io在線IDE,它就沒法把狀態(tài)對應(yīng)到url上。
緩存與本地存儲在單頁應(yīng)用的運作機制中,緩存是一個很重要的環(huán)節(jié)。
由于這類系統(tǒng)的前端部分幾乎全是靜態(tài)文件,所以它能夠有機會利用瀏覽器的緩存機制,而比如動態(tài)加載的界面模板,也完全可以做一些自定義的緩存機制,在非首次的請求中直接取緩存的版本,以加快加載速度。
甚至,也出現(xiàn)了一些方案,在動態(tài)加載JavaScript代碼的同時,把它們也緩存起來。比如Addy Osmani的這個basket.js,就利用了HTML5 localStorage作了js和css文件的緩存。
在單頁產(chǎn)品中,業(yè)務(wù)代碼也常常會需要跟本地存儲打交道,存儲一些臨時數(shù)據(jù),可以使用localStorage或者localStorageDB來簡化自己的業(yè)務(wù)代碼。
服務(wù)端通信傳統(tǒng)的Web產(chǎn)品通常使用JSONP或者AJAX這樣的方式與服務(wù)端通信,但在單頁Web應(yīng)用中,有很大一部分采用WebSocket這樣的實時通訊方式。
WebSocket與傳統(tǒng)基于HTTP的通信機制相比,有很大的優(yōu)勢。它可以讓服務(wù)端很便利地使用反向推送,前端只響應(yīng)確實產(chǎn)生業(yè)務(wù)數(shù)據(jù)的事件,減少一遍又一遍無意義的AJAX輪詢。
由于WebSocket只在比較先進的瀏覽器上被支持,有一些庫提供了在不同瀏覽器中的兼容方案,比如
http://socket.io,它在不支持WebSocket的瀏覽器上會降級成使用AJAX或JSONP等方式,對業(yè)務(wù)代碼完全透明、兼容。
內(nèi)存管理傳統(tǒng)的Web頁面一般是不需要考慮內(nèi)存的管理的,因為用戶的停留時間相對少,即使出現(xiàn)內(nèi)存泄漏,可能很快就被刷新頁面之類的操作沖掉了,但單頁應(yīng)用是不同的,它的用戶很可能會把它開一整天,因此,我們需要對其中的DOM操作、網(wǎng)絡(luò)連接等部分格外小心。
樣式的規(guī)劃在單頁應(yīng)用中,因為頁面的集成度高,所有頁面聚集到同一作用域,樣式的規(guī)劃也變得重要了。
樣式規(guī)劃主要是幾個方面:
基準樣式的分離
這里面主要包括瀏覽器樣式的重設(shè)、全局字體的設(shè)置、布局的基本約定和響應(yīng)式支持。
組件樣式的劃分
這里面是兩個層面的規(guī)劃,首先是各種界面組件及其子元素的樣式,其次是一些修飾樣式。組件樣式應(yīng)當盡量減少互相依賴,各組件的樣式允許冗余。
堆疊次序的管理
傳統(tǒng)Web頁面的特點是元素多,但是層次少,單頁應(yīng)用會有些不同。
在單頁應(yīng)用中,需要提前為各種UI組件規(guī)劃堆疊次序,也就是z-index,比如說,我們可能會有各種彈出對話框,浮動層,它們可能組合成各種堆疊狀態(tài)。新的對話框的z-index需要比舊的高,才能確保蓋在它上面。諸如此類,都需要我們對這些可能的遮蓋作規(guī)劃,那么,怎樣去規(guī)劃呢?
了解通信知識的人,應(yīng)當會知道,不同的頻率段被劃分給不同的通信方式使用,在一些國家,領(lǐng)空的使用也是有劃分的,我們也可以用同樣的方式來預(yù)先分段,不同類型的組件的z-index落到各自的區(qū)間,以避免它們的沖突。
單頁應(yīng)用的產(chǎn)品形態(tài)我們在開始的時候提到,存在著很多新型Web產(chǎn)品,使用單頁應(yīng)用的方式構(gòu)建,但實際上,這類產(chǎn)品不僅僅存在于Web上。點開Chrome商店,我們會發(fā)現(xiàn)很多離線應(yīng)用,這些產(chǎn)品都可以算是單頁應(yīng)用的體現(xiàn)。
除了各種瀏覽器插件,借助node-webkit這樣的外殼平臺,我們可以使用Web技術(shù)來構(gòu)建本地應(yīng)用,產(chǎn)品的主要部分仍然是我們熟悉的單頁應(yīng)用。
單頁應(yīng)用的流行程度正在逐漸增加,大家如果關(guān)注了一些初創(chuàng)型互聯(lián)網(wǎng)企業(yè),會發(fā)現(xiàn)其中很大一部分的產(chǎn)品模式是單頁化的。這種模式能帶給用戶流暢的體驗,在開發(fā)階段,對JavaScript技能水平要求較高。
單頁應(yīng)用開發(fā)過程中,前后端是天然分離的,雙方以API為分界。前端作為服務(wù)的消費者,后端作為服務(wù)的提供者。
在此模式下,前端將會推動后端的服務(wù)化。當后端不再承擔模板渲染、輸出頁面這樣工作的情況下,它可以更專注于所提供的API的實現(xiàn),而在這樣的情況下,Web前端與各種移動終端的地位對等,也逐漸使得后端API不必再為每個端作差異化設(shè)計了。
部署模式的改變在現(xiàn)在這個時代,我們已經(jīng)可以看到一種產(chǎn)品的出現(xiàn)了,那就是“無后端”的Web應(yīng)用。這是一種什么東西呢?基于這種理念,你的產(chǎn)品很可能只需要自己編寫靜態(tài)Web頁面,在某種BaaS(Backend as a Service)云平臺上定制服務(wù)端API和云存儲,集成這個平臺提供的SDK,通過AJAX等方式與之打交道,實現(xiàn)注冊認證、社交、消息推送、實時通信、云存儲等功能。
我們觀察一下這種模式,會發(fā)現(xiàn)前后端的部署已經(jīng)完全分離了,前端代碼完全靜態(tài)化,這意味著可以把它們放置到CDN上,訪問將大大地加速,而服務(wù)端托管在BaaS云上,開發(fā)者也不必去關(guān)注一些部署方面的繁瑣細節(jié)。
假設(shè)你是一名創(chuàng)業(yè)者,正在做的是一種實時協(xié)同的單頁產(chǎn)品,可以在云平臺上,快速定制后端服務(wù),把絕大部分寶貴的時間花在開發(fā)產(chǎn)品本身上。
單頁應(yīng)用的缺陷單頁應(yīng)用最根本的缺陷就是不利于SEO,因為界面的絕大部分都是動態(tài)生成的,所以搜索引擎很不容易索引它。
產(chǎn)品單頁化帶來的挑戰(zhàn)一個產(chǎn)品想要單頁化,首先是它必須適合單頁的形態(tài)。其次,在這個過程中,對開發(fā)模式會產(chǎn)生一些變更,對開發(fā)技能也會有一些要求。
如果你對編程感興趣或者想往編程方向發(fā)展,可以關(guān)注微信公眾號
【筑夢編程】,大家一起交流討論!小編也會每天定時更新既有趣又有用的編程知識!
開發(fā)者的JavaScript技能必須過關(guān),同時需要對組件化、設(shè)計模式有所認識,他所面對的不再是一個簡單的頁面,而是一個運行在瀏覽器環(huán)境中的桌面軟件。