揭祕React同構應用
隨著React和Redux為服務端渲染提供了優良特性,同構應用變得越來越普遍。作為開發者,即使採用的技術架構並不是基於服務端渲染的同構設計,也很有必要對同構設計進行了解並掌握其原理。
前後端架構設計和服務端渲染概念
服務端渲染或直出的概念越來越流行。在瞭解如何基於React實現服務端渲染之前,我們有必要在架構層面對服務端渲染的“前世今生”進行整體瞭解:為什麼會出現這樣一個概念;這個概念落地之後能解決什麼問題;服務端渲染和其他方式對比有何利弊等。
前後端配合技術的演進
早期的Web開發,架構設計簡單、直接,具體來講,就是頁面由JSP、PHP等工程師在服務端生成,瀏覽器只負責展現。那時候,前端工程師只需要給靜態頁面新增一些動態互動效果,很少會涉及資料邏輯等;而後端工程師負責頁面內容,即當用戶請求頁面時,後端進行處理並返回完整的靜態頁面。這些過程一般會依靠模板引擎來完成。因此,在那個時候,甚至沒有獨立的前端工程師職位。即使有的話,這種做法的缺點也很明顯,比如前後端分工職責不清。
如果由前端人員來開發模板,那麼前端將會極度依賴後端環境,難以實現開發效率的最大化,同時關於資料格式的溝通成本也相對較高。另外,這樣的架構模式對於前端技術的發揮和利用瀏覽器能力的空間是非常有限的。
隨著前端技術的飛速發展,尤其是AJAX和Node.js等技術的出現,一種前後端分離的架構模式應運而生。在這種模式下,前後端分工變得非常清晰,兩端的關鍵協作點是AJAX介面。下面我們以使用者訪問頁面為例來一步步瞭解這種模式,如下圖所示。
非服務端渲染訪問流程圖
在這種構架設計下,對頁面的請求處理分為以下幾個步驟。
- 瀏覽器請求頁面。
- 服務端返回“不包含頁面內容”的HTML檔案。
- 瀏覽器載入靜態頁面,解析HTML檔案。
- 在HTML檔案中遇見所需的CSS資源,進行請求並拉取資源。
- 在HTML檔案中遇見所需的JavaScript資源,進行請求並拉取指令碼。
- 當JavaScript檔案載入完成後,執行JavaScript指令碼。
- 在JavaScript指令碼中包含了對頁面所需資料的非同步請求,此時通過AJAX來獲取資料。
- 資料請求成功後由JavaScript指令碼完成資料處理,並根據資料渲染到頁面進行展現。
這樣的架構設計使得前後端開發可以並行進行,職責清晰——前端工作主要集中在瀏覽器端,前端開發只需要完成對測試資料的模擬,環境相對容易配置,能做到本地開發,脫離後端的支援;而後端專注於業務邏輯,負責API介面的實現。
然而任何技術架構和設計都不可能脫離時代而永遠存在,技術的演進一定會隨著發展愈演愈烈。這種架構模式在提升開發效率的同時,短板也很明顯,比如不利於SEO(Search EngineOptimization,搜尋引擎優化)和存在效能問題等。
SEO是一種通過了解搜尋引擎的運作規則來調整網站,以及提高目的網站在有關搜尋引擎內排名的方式。如今,網民主要通過搜尋引擎在網上查詢資訊和資源。SEO做得好,能夠直接提升網站在搜尋引擎搜尋結果中的展現排名,更有利於頁面的曝光。可是採用前後端分離的方式,由於頁面的資料內容主要由JavaScript指令碼動態生成,因此非常不利於搜尋引擎獲取該頁面的資訊,影響該頁面的SEO。
另外,在這種架構設計下,我們會發現來自瀏覽器端的請求增多了,體現在使用者體驗上,就是使用者必須等待JavaScript指令碼載入完成,且真正執行時才會發起資料請求。接下來,等待資料成功返回後,指令碼完成頁面內容渲染,使用者才可以得到最終頁面。這樣做直接降低了頁面首屏展現的時間,特別是在移動網際網路環境下,對首屏載入效能的影響很大。
為了解決上述問題,服務端渲染技術粉墨登場。
技術歷史總是驚人的相似
服務端渲染技術會把資料請求過程放在服務端,相對於前後端分離的方式,獲取資料更加提前,頁面模板結合資料的渲染處理也在服務端完成。結合React技術,基本的元件拼接在服務端完成,並最終輸出相對完整的HTML返回給瀏覽器端。接下來,進一步的元件渲染將在瀏覽器端完成。具體流程如下圖所示。
服務端渲染訪問流程圖
這樣做的好處非常明顯:當瀏覽器初次請求頁面後,使用者第一次拿到的HTML文件已經進行了初步內容渲染,這樣必然更有利於SEO優化,也解決了首屏的效能問題。
我們發現總的請求數並沒有改變,而是把瀏覽器的一部分資料請求移到了服務端。事實上,在服務端進行資料拉取的成本要遠遠小於瀏覽器端,而且傳輸更加高效,這也是效能提升的關鍵之處。
細心的讀者可能會發現,這裡所謂的服務端渲染與本章開始介紹的早期Web開發的服務端渲染傳統模式並沒有本質上的區別。事實確實如此,從某種程度上說,它是一種向傳統模式的迴歸,不過這種迴歸並不是倒退,而是一種螺旋式的發展。事實上,依靠React實現的服務端渲染也並不是簡單地渲染內容,在很大程度上它還實現了程式碼複用。
同構應用
下面我們將“服務端渲染”一詞替換為“同構”。其實,這兩個詞的背景和所表達的意義大體相同,但又有一定的差別:服務端渲染主要側重架構層面的實現,而同構更側重程式碼複用。
任何一種架構模式都是以服務業務需求為前提、以技術時代發展為背景的。它們各有利弊,具體採用哪一種模式,需要開發者深思熟慮,結合自身的實際情況進行選擇。
什麼是同構
隨著Node.js的異軍突起,前後端開發有了歸一化程式語言的基礎土壤,頁面模版、第三方依賴機制等都有實現前後端統一的契機。React率先引領了這種潮流,同構的概念也因此得以更廣泛的傳播。
需要讀者明白的是,同構應用並不是不需要瀏覽器端渲染內容,而是使服務端和瀏覽器端渲染達到一種平衡。那麼,怎麼理解這種平衡呢?
在伺服器上生成渲染內容,讓使用者儘早看到有資訊的頁面。一個完整的應用除包括純粹的靜態內容以外,還包括各種事件響應、使用者互動等。這就意味著在瀏覽器端一定還要執行JavaScript指令碼,以完成繫結事件、處理非同步互動等工作。
從效能及使用者體驗上來看,服務端渲染應該表達出頁面最主要、最核心、最基本的資訊;而瀏覽器端則需要針對互動完成進一步的頁面渲染、事件繫結等增強功能。所謂同構,就是指前後端共用一套程式碼或邏輯,而在這套程式碼或邏輯中,理想的狀況是在瀏覽器端進一步渲染的過程中,判斷已有的DOM結構和即將渲染出的結構是否相同,若相同,則不重新渲染DOM結構,只需要進行事件繫結即可。
從這個維度上講,同構和服務端渲染又有所區別,同構更像是服務端渲染和瀏覽器端渲染的交集,它彌補了服務端和瀏覽器端的差異,從而使得同一套程式碼或邏輯得以統一執行。同構的核心是“同一套程式碼”,這是脫離於兩端角度的另一個維度。
同構的優勢和劣勢
同構的優勢如下:
- 更好的效能。這裡的效能主要指渲染更加迅速、首屏展現的時間更快、檔案更少,以及檔案體積更小。
- SEO優化支援。服務端接收到請求後,會返回一個相對完整、包含了初始內容的HTML文件,所以更有利於搜尋引擎爬蟲獲取資訊,提高搜尋結果展現排名。同時,更快的頁面載入時間也有利於搜尋結果展現排名的提升。
- 實現更加靈活。服務端渲染只是直出頁面的初始內容,瀏覽器端仍然需要做後續工作,以完成頁面的最終展現。這樣服務端渲染和瀏覽器端渲染仍可以平衡,在很大程度上也能實現程式碼複用。
- 可維護性更強。因為藉助React等類庫,我們完全能夠實現大範圍的程式碼複用,避免了服務端和瀏覽器端同時維護兩套程式碼或邏輯。因此,整體程式碼量更少,維護成本更低。
- 對於低端機型更加友好。因為內容的初步渲染是在服務端完成的,所以對於低端機型更加友好,不至於頁面載入時出現白螢幕的狀況。
- 對於惡劣的網路環境更加友好。傳統的前後端分離方式,在所有的JavaScript指令碼下載並執行完畢後,才會呈現頁面內容,中間經歷了較多的網路請求,在惡劣的網路環境下,無疑增加了頁面呈現基本內容的難度。在這方面,同構應用顯然更有優勢。
- 更好的使用者體驗。為了更加合理地平衡服務端和瀏覽器端渲染內容,我們可以將頁面重要的核心部分設計在服務端完成,而次重要的互動部分可以在更重要的內容渲染完畢後,由瀏覽器端渲染或實現,這將有力地提升使用者體驗。
同構的劣勢如下:
- 服務端處理的邏輯增多,增加了複雜性。
- 服務端無法完全複用瀏覽器端程式碼。
- 增加了服務端的TTFB(Time To First Byte)時間。TTFB時間指的是從瀏覽器發起最初的網路請求,到從伺服器接收到第一個位元組的這段時間。它包含了TCP連線時間、傳送HTTP請求的時間和獲得響應訊息的第一個位元組的時間。因為對資料的獲取和對頁面初始內容的渲染,勢必會降低服務端返回的速度。
本文節選自博文視點新書《React狀態管理與同構實戰》。本書由知名技術博主侯策、顏海鏡親自執筆,得到百度公司副總裁沈抖、百度高階前端工程師董睿,以及阮一峰、狼叔、justjavac、小爝、顧軼靈等前端圈眾多專家大咖的聯合力薦。