直出是什麼?究竟是怎樣的性能優化?本文將結合從在瀏覽器輸入url,到展現最終頁面的過程來對其進行一步步分析,並將在手Q web 中的實際應用實踐進行總結。html
模式 1 – 經常使用模式前端
從用戶輸入 url 到展現最終頁面的過程,這種模式可簡單的分爲如下 5 部分react
用戶輸入 url,開始拉取靜態頁面git
靜態頁面加載完成後,解析文檔標籤,並開始拉取 CSS (通常 CSS 放於頭部)github
接着拉取 JS 文件(通常 JS 文件放於尾部)web
當 JS 加載完成,便開始執行 JS 內容,發出請求並拿到數據後端
具體流程圖以下react-native
這種處理形式應該佔據大多數,然而也很容易發現一個問題就是請求數多,先後依賴大,如必須等待 JS 加載完成後執行時纔會發起 數據請求,等待數據回來用戶才能夠展現最終頁面,這種強依賴的關係使得整個應用的首屏渲染耗時增長很多。瀏覽器
模式 2 – 數據直出緩存
在模式 1 中,第 1 點用戶輸入 url 時 server 端不作其餘處理直接返回 html ,在第 4 點向 server 請求獲取數據。那麼,一樣都是向 server 請求獲取,若是在第 1 點中將請求數據放在 server 上,將拿到的數據拼接到 HTML 上一併返回,那麼可減小在前端頁面上的一次數據請求時間。 這就是模式 2 – 數據直出所作的事,處理方式也很簡單
用戶輸入 url ,在 server 返回 HTML 前去請求獲取頁面須要的數據
將數據拼接到 HTML 上 並 一塊兒返回給前端 (能夠插入 script 標籤將數據添加到全局變量上,或放到某個標籤的 data 屬性中,如 <body data-serverData = ‘{list:[1,2,3]}’ >)
具體可下面的流程圖看出這種模式下
這種模式與模式1 相比,減小了這兩種模式請求數據的耗時差距。這塊差距有多少呢?
發起一個 HTTP 的網絡請求過程
DNS解析(100~200ms能夠緩存) | | 創建TCP連接 (三次握手100~200ms ) | | HTTP Request( 半個RTT ) | | HTTP Response( RTT 不肯定優化空間 )
注: RTT 爲 Round-trip time 縮寫,表示一個數據包從發出到返回所用的時間。
HTTP 請求在先後端發出,差距有多少?
由上面對 HTTP 的網絡請求過程可看到創建一次完整的請求返回在耗時上明顯的,特別是外網用戶在進行 HTTP 請求時,因爲網絡等因素的影響,在網絡鏈接及傳輸上將花費不少時間。而在服務端進行數據拉取,即便一樣是 HTTP 請求,因爲後端之間是處於同一個內網上的,因此傳輸十分高效,這是差距來源的大頭,是優化的剛需。
模式 3 – 直出 (服務端渲染)
模式 2 中將依賴於JS文件加載回來才能去發起的數據請求挪到 server 中,數據隨着 HTML 一併返回。而後等待 JS 文件加載完成,JS 將服務端已給到的數據與HTML結合處理,生成最終的頁面文檔。
數據請求能放到 server 上,對於數據與HTML結合處理也能夠在server上作,從而減小等待 JS 文件的加載時間。 這就是模式3 – 直出 (服務端渲染),主要處理以下
server 上獲取數據並將數據與頁面模板結合,在服務端渲染成最終的 HTML
能夠從下圖看出,頁面的首屏展現再也不須要等待 JS 文件回來,優化減小了這塊時間
經過以上模式,將模式 1 – 經常使用模式中的第 3 和 4 點耗時進行了優化,那麼能夠再繼續優化嗎?
在頁面文檔不大狀況下,可將CSS內聯到HTML中,這是優化請求量的作法。直出稍微不一樣的是須要考慮的是服務端最終渲染出來的文檔的大小,在範圍內也可將 CSS 文件內聯到 HTML 中。這樣的話,便優化了 CSS 的獲取時間,以下圖
小結
直出可以將經常使用模式優化到剩下了一次 HTML 請求,加快首屏渲染時間,使用服務端渲染,還可以優化前端渲染難以克服的 SEO 問題。而不論是簡單的 數據直出 或是 服務端渲染直出 都能使頁面的性能優化獲得較大提升,如下將從實際應用中進行說明。
以手Q家校羣的數據直出優化爲例
因爲項目上線時間緊,因此在第一次優化上使用了數據直出的簡單方式來優化首屏渲染時間。具體處理與 模式 2 數據直出方式 一致,與其不一樣的是這裏使用了由 AlloyTeam 開發的 基於KOA的玄武直出服務 來做爲前端與服務端間的中間層。形式以下
使用這種中間層的方式,在項目的開發過程當中依然可以使用先後端分離的方式,開發完後再將頁面請求指向這個中間層服務上。中間層服務主要作了上述 模式 2 – 數據直出 中的處理
使用前端文件及調用服務端作好的拉取數據接口
因爲該中間層服務與具體server部署在相同的內網上,因此它們直接的數據交互是十分高效的,從而可達到 模式 2 – 數據直出 中所述的優化。
另外一點,作爲中間層玄武直出服務經過公司的L5負載均衡服務,完美兼容直出與非直出版本,即當直出服務掛掉了,也能夠順利走非直出版本,確保基本的用戶體驗,也可以更好的支持 A/BTest。
性能數據
簡單的數據方式直出一樣迎來了較大的性能提高,手Q家校羣列表頁在首屏渲染完成時間上,相比於優化前的版本,數據直出有大概 650ms 的優化,提高約 35% 的性能。
總結
在先後端沒有分離時 使用後端渲染出模板的方式是與文中所述的直出方案效果是一致的,先後端分離後淡化了這種思想,Node 的發展讓更多的前端開始作後端事情,直出的方式也愈來愈被重視了。
歷史的車輪滾滾向前,直出方案看似回到了服務端渲染的原點,其實是在之前的基礎上盤旋上升。有了更多的能力,即可以有更多的思考。期待前端會愈來愈強大,這不,react-native也讓前端開始着手客戶端的事兒了 ~
後記
手Q家校羣使用 React + Redux + Webpack架構,既然是 React,確定不可忽略 React 同構 (服務端渲染) 關於React 同構直出的具體實踐,我將其總結在另一篇文章上,可點擊查看 Web性能優化之服務端渲染 React 同構直出(https://github.com/joeyguo/blog/issues/9)
對於文章一開始說起的前端路由,對路由的實現原理感興趣的也可點擊查看 前端路由實現與 react-router 源碼分析