單頁式應用程式

單頁式應用程式(英語:single-page application,縮寫SPA)是一種網路應用程式網站的模型,它透過動態重寫當前頁面來與使用者互動,而非傳統的從伺服器重新載入整個新頁面。這種方法避免了頁面之間切換打斷使用者體驗,使應用程式更像一個桌面應用程式。在單頁式應用程式中,所有必要的代碼(HTMLJavaScriptCSS)都透過單個頁面的載入而檢索[1],或者依需(通常是為回應使用者操作)動態載入適當的資源並添加到頁面。儘管可以用位置雜湊HTML5歷史API英語Comparison of layout engines (HTML5)#APIs來提供應用程式中單獨邏輯頁面的感知和導覽能力,但頁面在過程中的任何時間點都不會重新載入,也不會將控制轉移到其他頁面。[2]與單頁式應用程式的互動通常涉及到與網頁伺服器後端的動態通訊。

歷史

編輯

儘管早在2003年就討論過這個概念,但單頁式應用程式(single-page application)一詞的起源並不明晰。[3]Stuart (stunix) Morris英語Stuart (stunix) Morris在2002年4月編寫的自主網站slashdotslash.com有著相同的目標和功能[4];同年稍晚,Lucas Birdeau、Kevin Hakman、Michael Peachey和Evan Yeh在美國專利8,136,109中描述了一個單頁式應用程式的實作概念。[5]

在網頁瀏覽器中可以使用JavaScript顯示使用者介面(UI)、執行應用程式邏輯,以及與Web伺服器通訊。成熟的開源程式庫支援構建一個單頁式應用程式,從而減少開發人員要編寫的JavaScript代碼量。

技術舉措

編輯

現今已有各種技術可以使網頁瀏覽器停留在單個頁面,並同時支援應用程式與伺服器通訊。

JavaScript框架

編輯

諸如AngularEmber.jsMeteor.js英語Meteor (web framework)ExtJSReact等網頁瀏覽器端的JavaScript框架採納了單頁式應用程式(SPA)原則。

  • Angular是一個全面的客戶端側框架。其模板基於雙向UI資料繫結英語UI data binding。資料繫結是一種自動方法,在模型改變時更新視圖,以及在視圖改變時更新模型。其HTML模板在瀏覽器中編譯。編譯步驟建立純HTML,瀏覽器將其重新彩現到即時視圖。該步驟會在隨後的頁面瀏覽中重複。在傳統的伺服器端HTML編程中,控制器和模型等概念在伺服器行程中進行互動以產生新的HTML視圖。在Angular框架中,控制器和模型狀態在客戶端的瀏覽器中維護,從而使生成新頁面不依賴與伺服器的互動。
  • Ember.js是基於模型-視圖-控制器(MVC)軟體架構模型的客戶端側JavaScript Web應用程式框架。它允許開發人員在一個框架中透過常用的習慣用語和最佳實踐來建立可伸縮的單頁面應用程式。該框架提供豐富的對象模型、宣告性雙向資料繫結、計算屬性,Handlebars.js提供的自動更新模板,以及一個路由器管理應用程式狀態。
  • Meteor.js英語Meteor (web framework)是一個專門為單頁式應用程式設計的Full Stack(客戶端-伺服器)JavaScript框架。它具有比Angular、Ember或ReactJS更簡單的資料繫結特性[6],並且使用Distributed Data Protocol英語Distributed Data Protocol[7]和一個發布/訂閱來自動將資料更改傳播到客戶端,無需開發人員編寫任何同步代碼。Full Stack反應確保從資料庫到模板的所有層都可以在必要時自動更新。諸如伺服器端彩現[8]等生態系統包則解決搜尋引擎最佳化(SEO)等問題。
  • Aurelia頁面存檔備份,存於網際網路檔案館)是一個適用於行動裝置、桌面和網頁的JavaScript客戶端框架。它類似AngularJS,但更新、更符合標準,並採用模組化舉措。Aurelia使用下一代ECMAScript編寫。[來源請求]
  • Vue.js(通常稱為Vue)是一個用於構建使用者介面的開源漸進式JavaScript框架。
  • React(通常寫為React.js或ReactJS)是一個構建使用者介面的JavaScript函式庫。它由FacebookInstagram和個人開發者以及企業社群維護。React最大的優勢是易於使用——基本上任何熟悉HTML的開發人員都可以建立React應用程式。另一個所稱的優勢是可能使用相同的技術堆疊來同時建立Web與行動應用程式。有多家公司使用React和Redux程式庫來讓開發人員建立複雜但可延伸的Web應用程式。[9]
  • Fulcro英語Fulcro (web framework)是一個Full Stack程式庫,它採用Netflix的Falcor,Facebook的Relay和Om Next對反應性,功能性,資料驅動軟體進行改編的資料驅動原則。[10][需要較佳來源]

目前最常採用Ajax技術。主要從JavaScript使用XMLHttpRequest/ActiveX對象(已不建議使用),其他Ajax方法包括使用IFRAME或script HTML元素。jQuery等流行的程式庫可以為不同廠商的瀏覽器的Ajax行為標準化,進一步推廣了Ajax技術。

Websocket

編輯

WebSocket是HTML5規範中的一個雙向有狀態即時客戶端與伺服器通訊技術,在效能和簡單性方面優於Ajax[11]

伺服器傳送事件

編輯

伺服器傳送事件英語Server-sent events(SSE)是一種伺服器向瀏覽器客戶端發起資料傳輸的技術。一旦建立了初始連接,事件流將保持打開狀態,直到客戶端關閉。該技術透過傳統的HTTP傳送,並具有WebSockets缺乏的各種功能,例如自動重新連接、事件ID以及傳送任意事件的能力。[12]

瀏覽器外掛程式

編輯

可以使用瀏覽器外掛程式技術(如SilverlightFlashJava applet)實現對伺服器的非同步呼叫。此種方法在業界已經過時。

資料傳輸(XML、JSON)

編輯

對伺服器的請求通常會帶來原始資料(如XMLJSON)或傳回新的HTML。在伺服器傳回HTML時,客戶端上的JavaScript將更新DOM的部分割區域。[13]在傳回原始資料時,客戶端側的JavaScript通常將其轉換為HTML,然後用它來更新DOM的部分區域。

伺服器架構

編輯

瘦伺服器架構

編輯

SPA技術將邏輯從伺服器轉移到了客戶端。這導致Web伺服器發展為一個純資料API或Web服務。這種架構的轉變在一些圈子中被稱為「瘦伺服器架構」,以強調複雜性已從伺服器端轉移到客戶端,並認為這最終降低了系統的整體複雜性。

胖的有狀態伺服器架構

編輯

這種設計是伺服器在主記憶體中儲存必要的客戶端所處狀態。這種模式下,當任何請求到達伺服器(通常因使用者操作)時,伺服器傳送適當的HTML和/或JavaScript,以及具體的更改,以使客戶端達到新的期望狀態(如添加、刪除或更新部分客戶端的DOM)。與此同時更新伺服器中的狀態。這種設計下的大部分邏輯都在伺服器上執行,HTML通常也在伺服器上呈現。在某些方面,伺服器是類比Web瀏覽器,接收事件並執行伺服器狀態下的增量更改,將這些更改自動傳播到客戶端。

這種方法需要更多的伺服器主記憶體和處理能力,但優點是簡化的開發模型,因為:1、應用程式通常完全在伺服器中編寫;2、伺服器中的資料和UI狀態在相同的主記憶體空間中共享,不需要自訂客戶端/伺服器通訊隧道。

胖的無狀態伺服器架構

編輯

這是有狀態伺服器方法的變體。客戶端頁面通常透過Ajax請求將表示其當前狀態的資料傳送到伺服器。伺服器使用這些資料能夠重新構建需要修改的頁面部分,並生成必要的資料或代碼(如作為JSON或JavaScript),將其傳回給客戶端使其它達到新的狀態。

此方法需要將更多資料傳送給伺服器,並可能需要更多計算資源才能部分或完全重建在伺服器中的客戶端頁面狀態。不過,這種方法更容易擴充,因為伺服器中不存在每個客戶端的頁面資料,因此可以將Ajax請求分派到不同的伺服器節點,而無需對談資料共享或伺服器關聯。

本機執行

編輯

部分單頁式應用程式可以使用file URI方案英語File URI scheme依賴本地檔案執行。這使使用者可以從伺服器上下載單頁式應用程式並在本地裝置中執行,而不需要依賴與伺服器的網路連接。這類單頁式應用程式如果想要儲存或更新資料,則必須使用基於瀏覽器的網頁儲存。這些應用受益於HTML5提供的進階功能。[14]

SPA模式帶來的挑戰

編輯

由於單頁式應用程式偏離了最初為瀏覽器設計的無狀態頁面重繪模型,所以帶來了一些新挑戰。每個問題都有一個或一些有效的解決方案[15]

  • 客戶端JavaScript程式庫解決各種問題。
  • 專門針對單頁式應用程式模型研發的伺服器側Web框架。[16][17][18]
  • 瀏覽器的發展以及為單頁式應用程式模型設計的HTML5規範。[19]

搜尋引擎最佳化

編輯

由於一些流行的網路搜尋引擎的爬蟲缺乏JavaScript執行能力[20]搜尋引擎最佳化(SEO)已成為面向公眾的網站採用單頁式應用程式模型必須面對的一個問題。[21]

在2009至2015年間,Google網站管理員提出並推薦了一個「AJAX爬取方案」[22][23],其中使用起始為驚嘆號的片段識別碼(#!)來標識有狀態的AJAX頁面。單頁式應用程式網站必須實作特殊行為才能允許搜尋引擎的爬取工具提取相關的中繼資料。對於不支援此URL雜湊方案的搜尋引擎,單頁式應用程式的雜湊網址會被視而不見。包括Jeni Tennison在內的許多作者認為這些「hash-bang」URI在W3C中被認為是存在問題的,因為它們使那些沒有在瀏覽器中啟用JavaScript的人無法訪問頁面。這也影響HTTP參照位址標頭,因為瀏覽器不允許在Referer標頭中傳送片段識別碼。[24]2015年,Google不再建議使用雜湊AJAX爬取方案。[25]

還有一些周邊方法可以使網站表現為可抓取,它們都涉及建立反應單頁式應用程式內容的獨立HTML頁面。伺服器可以建立基於HTML的網站版本並將此提供給爬取工具,也可以使用PhantomJS等無頭(headless)瀏覽器執行JavaScript應用程式並輸出生成的HTML。

這種方法需要付出不小的努力,最終可能給大型、複雜的網站帶來很大的維護成本,並且也有潛在風險。如果伺服器生成的HTML被認為與單頁式應用程式存在較大差異,則網站排名將受到處罰。執行PhantomJS來輸出HTML可能會降低頁面的回應速度,一些搜尋引擎(尤其是Google)可能因此對排名降級。[26]

客戶端/伺服器代碼分割區

編輯

增加伺服器與客戶端之間所共享代碼量的一種方法是使用如Mustache英語Mustache (template system)Handlebars英語Handlebars (template system)等無邏輯模板語言。這種模板可以用不同的代管語言來呈現,如伺服器上的Ruby和客戶端的JavaScript。但是,單純共享模板通常需要重複使用業務邏輯來選擇正確的模板,並使用資料填充它們。僅更新頁面的一小部分(諸如大型模板中的一個文字的值)時,從模板呈現可能會產生負面的效能影響。替換整個模板也可能會干擾使用者的選擇或游標位置,只更新變更的值則可能不會。為避免這些問題,應用程式可以使用UI資料繫結英語UI data binding或粒度DOM操作來更新頁面的相應部分,而不是重新呈現整個模板。

瀏覽器歷史記錄

編輯

根據單頁式應用程式(SPA)模型的定義,它只有「單個頁面」,因此這打破了瀏覽器為頁面歷史記錄導覽所設計的「上一頁、下一頁」功能。當使用者按下上一頁按鈕時,可能會遇到可用性障礙,頁面可能返回真正的上一個頁面,而非使用者所期望的上一個頁面。

傳統的解決方案是不斷更改瀏覽器網址(URL)的雜湊片段識別碼以保持與當前的螢幕狀態一致。這種方案可以透過JavaScript實現,並在瀏覽器中建立起網址歷史事件。只要單頁式應用程式能根據網址雜湊所包含的資訊重新生成相同的螢幕狀態,就能實現預期的上一頁按鈕行為。

而為進一步解決此問題,HTML5規範引入了pushState頁面存檔備份,存於網際網路檔案館)和replaceState頁面存檔備份,存於網際網路檔案館)來提供程式碼對實際網址和瀏覽器歷史的訪問。

分析

編輯

諸如Google分析等分析工具高度依賴在瀏覽器中載入全新的頁面,而單頁式應用程式的工作方式不同於此。

初次載入的速度

編輯

單頁式應用程式的第一頁載入會比基於伺服器的應用程式慢。這是因為首次載入必須先拿到框架和應用程式的代碼,再在瀏覽器中呈現所需的視圖。基於伺服器的應用程式只需將所需的HTML推播到瀏覽器,從而減少了延遲和下載用時。

加快頁面載入速度

編輯

有一些方法可以加快單頁式應用程式的初次載入速度,比如採用多項快取措施、需要時再載入某些模組(延遲載入)。但這依舊不可能擺脫需要下載框架的事實,某些部分必須在呈現前下載。這是一個「現在支付,或者稍後支付」的問題。效能與等待時間的權衡是開發者必須作出的權衡。

頁面生命周期

編輯

單頁式應用程式在初始頁面載入時被完全載入,然後頁面區域被替換或更新為按需從伺服器載入的新頁面片段。為避免過度下載未使用的功能,單頁式應用程式通常會逐漸下載更多內容,如所需要的功能、頁面的一小塊,或者完整的一頁。

單頁式應用程式(SPA)舉措與原生桌面應用程式所使用的多檔案介面(MDI)呈現技術類似。

參考資料

編輯
  1. ^ Flanagan, David, "JavaScript - The Definitive Guide", 5th ed., O'Reilly, Sebastopol, CA, 2006, p.497
  2. ^ Fixing the Back Button: SPA Behavior using Location Hash. [2016-01-18]. (原始內容存檔於2016-02-13) (美國英語). 
  3. ^ Inner-Browsing: Extending Web Browsing the Navigation Paradigm. [2011-02-03]. (原始內容存檔於2014-03-27). 
  4. ^ Slashdotslash.com: A self contained website using DHTML. [2012-07-06]. 
  5. ^ US patent 8,136,109. [2002-04-12]. (原始內容存檔於2017-08-15). 
  6. ^ Meteor Blaze. [2018-02-25]. (原始內容存檔於2020-12-05). Meteor Blaze is a powerful library for creating live-updating user interfaces. Blaze fulfills the same purpose as Angular, Backbone, Ember, React, Polymer, or Knockout, but is much easier to use. We built it because we thought that other libraries made user interface programming unnecessarily difficult and confusing. 
  7. ^ Introducing DDP頁面存檔備份,存於網際網路檔案館), March 21, 2012
  8. ^ Server Side Rendering for Meteor. [31 January 2015]. (原始內容存檔於2015年3月20日). 
  9. ^ blak-it.com, BLAKIT,. Single-page applications vs. multiple-page applications: pros, cons, pitfalls - BLAKIT - IT Solutions. BLAKIT - IT Solutions. 2017-10-17 [2017-10-19]. (原始內容存檔於2017-10-19) (美國英語). 
  10. ^ The Benefits of Fulcro. 2017-11-02 [2018-02-25]. (原始內容存檔於2017-11-07). 
  11. ^ Real-Time Monitoring using AJAX and WebSockets. [2016-06-01]. (原始內容存檔於2018-11-30). 
  12. ^ Server-Sent Events. W3C. 17 July 2013 [2018-02-25]. (原始內容存檔於2021-02-02). 
  13. ^ Using InnerHTML. [2016-01-21]. (原始內容存檔於2021-01-27). 
  14. ^ Unhosted web apps. [2018-02-25]. (原始內容存檔於2021-01-25). 
  15. ^ The Single Page Interface Manifesto. [2014-04-25]. (原始內容存檔於2020-11-12). 
  16. ^ Derby. [2011-12-11]. (原始內容存檔於2015-08-01). 
  17. ^ Sails.js. [2013-02-20]. (原始內容存檔於2020-12-17). 
  18. ^ Tutorial: Single Page Interface Web Site With ItsNat. [2011-01-13]. (原始內容存檔於2016-03-12). 
  19. ^ HTML5
  20. ^ What the user sees, what the crawler sees. [January 6, 2014]. (原始內容存檔於2020-12-12). the browser can execute JavaScript and produce content on the fly - the crawler cannot 
  21. ^ Making Ajax Applications Crawlable. [January 6, 2014]. (原始內容存檔於2020-12-12). Historically, Ajax applications have been difficult for search engines to process because Ajax content is produced 
  22. ^ Proposal for making AJAX crawlable. Google. October 7, 2009 [July 13, 2011]. (原始內容存檔於2020-12-12). 
  23. ^ (Specifications) Making AJAX Applications Crawlable. Google. [March 4, 2013]. (原始內容存檔於2020-12-12). 
  24. ^ Hash URIs. W3C Blog. May 12, 2011 [July 13, 2011]. (原始內容存檔於2013-09-01). 
  25. ^ Deprecating our AJAX crawling scheme. Official Google Webmaster Central Blog. [2017-02-23]. (原始內容存檔於2021-02-17) (美國英語). 
  26. ^ Holmes, Simone (2015). Getting MEAN with Mongo, Express, Angular, and Node. Manning Publications. ISBN 978-1-6172-9203-3

外部連結

編輯