自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Web 現(xiàn)代應(yīng)用程序架構(gòu)下的性能優(yōu)化,漸進(jìn)式的極致藝術(shù)

新聞 前端
本文是谷歌工程師帶來的現(xiàn)代應(yīng)用架構(gòu)體系下的優(yōu)化相關(guān)演講的總結(jié),演講介紹了以下優(yōu)化手段。

前言

本文是 Rendering on the Web: Performance Implications of Application Architecture (Google I/O ’19) [1] 這篇谷歌工程師帶來的現(xiàn)代應(yīng)用架構(gòu)體系下的優(yōu)化相關(guān)演講的總結(jié),演講介紹了以下優(yōu)化手段:

  • 預(yù)渲染

  • 同構(gòu)渲染

  • 流式渲染

  • 漸進(jìn)式注水(非常精彩)

應(yīng)用架構(gòu)體系

當(dāng)我們討論「應(yīng)用架構(gòu)」的時(shí)候,可以理解為通過以下幾個(gè)部分組合來構(gòu)建網(wǎng)站。

  1. Component model 組件模型。
  2. Rendering and loading 渲染和加載。
  3. Routing and transitions 路由和過渡。
  4. Data/state management 數(shù)據(jù)、狀態(tài)的管理。

性能指標(biāo)

在分析頁面渲染性能之前,先了解一下幾個(gè)比較重要的指標(biāo),方便下文理解:

  1. FP : First Paint,是 Paint Timing API 的一部分,是頁面導(dǎo)航與瀏覽器將該網(wǎng)頁的第一個(gè)像素渲染到屏幕上所用的中間時(shí),渲染是任何與輸入網(wǎng)頁導(dǎo)航前的屏幕上的內(nèi)容不同的內(nèi)容。

  2. FCP : First Contentful Paint,首次有內(nèi)容的渲染是當(dāng)瀏覽器渲染 DOM 第一塊內(nèi)容,第一次回饋用戶頁面正在載入。

  3. TTI : Time to interactive 第一次可交互時(shí)間,此時(shí)用戶可以真正的觸發(fā) DOM 元素的事件,和頁面進(jìn)行交互。

  4. FID : First Input Delay 第一輸入延遲測(cè)量用戶首次與您的站點(diǎn)交互時(shí)的時(shí)間(即,當(dāng)他們單擊鏈接,點(diǎn)擊按鈕或使用自定義的 JavaScript 驅(qū)動(dòng)控件時(shí))到瀏覽器實(shí)際能夠的時(shí)間回應(yīng)這種互動(dòng)。

  5. TTFB : Time to First Byte 首字節(jié)時(shí)間,顧名思義,是指從客戶端開始和服務(wù)端交互到服務(wù)端開始向客戶端瀏覽器傳輸數(shù)據(jù)的時(shí)間(包括 DNS、socket 連接和請(qǐng)求響應(yīng)時(shí)間),是能夠反映服務(wù)端響應(yīng)速度的重要指標(biāo)。

如果你還不太熟悉這些指標(biāo)也沒關(guān)系,接下來的內(nèi)容中,會(huì)結(jié)合實(shí)際用例分析這些指標(biāo)。

渲染開銷 The cost of rendering

客戶端渲染 Client-side rendering

從服務(wù)端獲取 HTML、CSS、JavaScript 都是需要成本的,以一個(gè) CSR(客戶端渲染)的網(wǎng)站為例,客戶端渲染的網(wǎng)站依賴框架庫(bundle)、應(yīng)用程序(app)來進(jìn)行初始化渲染,假設(shè)它有 1MB 的 JavaScript Bundle 代碼,那么只有當(dāng)這一大段的代碼加載并執(zhí)行完成以后,用戶才能看到頁面。

它的結(jié)構(gòu)一般如下:

分析一下它的流程:

  1. 用戶輸入網(wǎng)址進(jìn)入網(wǎng)站,拉取 HTML 資源。

  1. HTML 資源中發(fā)現(xiàn) script 標(biāo)簽加載的 bundle 再一次發(fā)起請(qǐng)求拉取 bundle。此時(shí)也是性能統(tǒng)計(jì)指標(biāo)中的 FP 完成。

在這個(gè)階段,頁面基本上是沒什么意義的,當(dāng)然你也可以放置一些靜態(tài)的骨架屏或者加載提示,來友好的提示用戶。

  1. JavaScript bundle 下載并執(zhí)行完畢,此時(shí)頁面才真正渲染出有意義的內(nèi)容。對(duì)應(yīng) FCP 完成。

當(dāng)框架對(duì) DOM 節(jié)點(diǎn)添加各類事件綁定后,用戶才真正可以和頁面交互,此時(shí)也對(duì)應(yīng) TTI 完成。

它的 缺點(diǎn) 在于,直到整個(gè) JavaScript 依賴執(zhí)行完成之前,用戶都看不到什么有意義的內(nèi)容。

服務(wù)端同構(gòu)渲染 SSR with Hydration

基于以上客戶端渲染的缺點(diǎn)以及用戶對(duì)于 CSR 應(yīng)用交互更加豐富的需求,于是誕生了集 SSR 和 CSR 的 性能、SEO、數(shù)據(jù)獲取 的優(yōu)點(diǎn)與一身的「 同構(gòu)渲染 」,簡單點(diǎn)說,就是:

  1. 第一次請(qǐng)求,在服務(wù)端就利用框架提供的服務(wù)端渲染能力,直接原地請(qǐng)求數(shù)據(jù),生成包含完整內(nèi)容的 html 頁面,用戶不需要等待框架的 js 加載就可以看到內(nèi)容。

  2. 等到頁面渲染后,再利用框架提供的 Hydration(注水)能力,讓服務(wù)端返回的“干癟”的 HTML 注冊(cè)事件等等,變的豐富起來,擁有了各種事件后,就和傳統(tǒng) CSR 一樣擁有了豐富多彩的客戶端交互。

在同構(gòu)應(yīng)用中,只要 HTML 頁面返回,用戶就可以看到豐富多彩的頁面:

而 JavaScript 加載完畢后,用戶就可以和這些內(nèi)容進(jìn)行交互(比如點(diǎn)擊放大、跳轉(zhuǎn)頁面等等……)

代碼對(duì)比

典型的 CSR React 應(yīng)用的代碼是這樣的:

而 SSR 的代碼則需要服務(wù)端的配合,

先由服務(wù)端通過 ReactDOMServer.renderToString 在服務(wù)端把組件給序列化成 html 字符串,返回給前端:

前端通過 hydrate 注水,使得功能交互變的完整:

Vue 的 SSR 也是同理:

同構(gòu)的缺陷

至此看來,難道同構(gòu)應(yīng)用就是完美的嗎?當(dāng)然不是,其實(shí)普通的同構(gòu)應(yīng)用只是提升了 FCP 也就是用戶看到內(nèi)容的速度,但是卻還是要等到框架代碼下載完成, hydrate 注水完畢等一系列過程執(zhí)行完畢以后才能真正的 可交互 。

并且對(duì)于 FID 也就是 First Input Delay 第一輸入延遲這個(gè)指標(biāo)來說,由于 SSR 快速渲染出內(nèi)容,更容易讓用戶誤以為頁面已經(jīng)是可交互狀態(tài),反而會(huì)使「用戶第一次點(diǎn)擊 - 瀏覽器響應(yīng)事件」 這個(gè)時(shí)間變得更久。

因此,同構(gòu)應(yīng)用很可能變成一把「雙刃劍」。

下面我們來討論一些方案。

Pre-rendering 預(yù)渲染。

對(duì)于不經(jīng)常發(fā)生變化的內(nèi)容來說,使用預(yù)渲染是一種很好的辦法,它在代碼構(gòu)建時(shí)就通過框架能力生成好靜態(tài)的 HTML 頁面,而不是像同構(gòu)應(yīng)用那樣在用戶請(qǐng)求頁面時(shí)再生成,這讓它可以幾乎立刻返回頁面。

當(dāng)然它也有很大的限制:

  1. 只適用于靜態(tài)頁面。

  2. 需要提前列舉出需要預(yù)渲染的 URLs。

流式渲染 Streaming

流式渲染可以讓服務(wù)端對(duì)大塊的內(nèi)容分片發(fā)送,使得客戶端不需要完整的接收到 HTML,而是接受到第一部分時(shí)就開始渲染,這大大提升了 TTFB 首字節(jié)時(shí)間。

在 React 中,可以通過 renderToNodeStream 來使用流式渲染:

漸進(jìn)式注水 Progressive Hydration

我們知道 hydrate 的過程需要遍歷整顆 React 節(jié)點(diǎn)樹來添加事件,這在頁面很大的情況下耗費(fèi)的時(shí)間一定是很長的,我們能否先只對(duì)關(guān)鍵的部分,比如視圖中可見的部分,進(jìn)行「注水」,讓這部分先一步可以進(jìn)行交互?

想象一下它的特點(diǎn):

  1. 組件級(jí)別的漸進(jìn)式注水。

  2. 服務(wù)端依舊整頁渲染。

  3. 頁面可以根據(jù)優(yōu)先級(jí)來分片“啟動(dòng)”組件。

通過一張動(dòng)圖來直觀的感受一下普通注水(左)和漸進(jìn)式注水(右)的區(qū)別:

可以看到用戶第一次可以交互的時(shí)間大大的提前了。

光說不做假把式,我們看看用 React 完成這個(gè)功能的代碼,首先我們需要準(zhǔn)備一個(gè)組件 Hydrator 用來實(shí)現(xiàn)當(dāng)某個(gè)組件 進(jìn)入視圖范圍以后 再進(jìn)行注水。

首先來看看應(yīng)用的整體結(jié)構(gòu): 

  1. let load = () => import('./stream'); 
  2. let Hydrator = ClientHydrator; 
  3.  
  4. if (typeof window === 'undefined') { 
  5.   Hydrator = ServerHydrator; 
  6.   load = () => require('./stream'); 
  7.  
  8. export default function App() { 
  9.   return ( 
  10.     <div id="app"
  11.       <Header /> 
  12.       <Intro /> 
  13.       <Hydrator load={load} /> 
  14.     </div> 
  15.   ); 

根據(jù)客戶端和服務(wù)端的環(huán)境區(qū)分使用不同的 Hydrator ,在服務(wù)端就直接返回普通的 html 文本: 

  1. function interopDefault(mod) { 
  2.   return (mod && mod.default) || mod; 
  3.  
  4. export function ServerHydrator({ load, ...props }) { 
  5.   const Child = interopDefault(load()); 
  6.   return ( 
  7.     <section> 
  8.       <Child {...props} /> 
  9.     </section> 
  10.   ); 

而客戶端,則需要實(shí)現(xiàn)漸進(jìn)式注水的關(guān)鍵部分:

  1. export class Hydrator extends React.Component { 
  2.   render() { 
  3.     return ( 
  4.       <section 
  5.         ref={c => (this.root = c)} 
  6.         dangerouslySetInnerHTML={{ __html: '' }} 
  7.         suppressHydrationWarning 
  8.       /> 
  9.     ); 
  10.   } 

首先 render 部分,利用 dangerouslySetInnerHTML 來使得這部分初始化為空的 html 文本,并且由于 server 端肯定還是和往常一樣全量渲染內(nèi)容,而客戶端由于初始化需要先不做任何處理,會(huì)導(dǎo)致 React 內(nèi)部對(duì)于服務(wù)端內(nèi)容和客戶端內(nèi)容的「一致性檢測(cè)」失敗。

而利用 dangerouslySetInnerHTML 的特性,會(huì)讓 React 不再進(jìn)一步 hydrate 遍歷 children 而是直接沿用服務(wù)端渲染返回的 HTML,保證在注水前渲染的樣式也是 OK 的。

再利用 suppressHydrationWarning 取消 React 對(duì)于內(nèi)容一致性檢測(cè)失敗的警告。

  1. export class Hydrator extends React.Component { 
  2.   componentDidMount() { 
  3.     new IntersectionObserver(async ([entry], obs) => { 
  4.       if (!entry.isIntersecting) return
  5.       obs.unobserve(this.root); 
  6.  
  7.       const { load, ...props } = this.props; 
  8.       const Child = interopDefault(await load()); 
  9.       ReactDOM.hydrate(<Child {...props} />, this.root); 
  10.     }).observe(this.root); 
  11.   } 
  12.  
  13.   render() { 
  14.     return ( 
  15.       <section 
  16.         ref={c => (this.root = c)} 
  17.         dangerouslySetInnerHTML={{ __html: '' }} 
  18.         suppressHydrationWarning 
  19.       /> 
  20.     ); 
  21.   } 

接下來,組件在客戶端初始化的時(shí)候,利用 IntersectionObserver 監(jiān)控組件元素是否進(jìn)入視圖,一旦進(jìn)入視圖了,才會(huì)動(dòng)態(tài)的去 import 組件,并且利用 ReactDOM.hydrate 來真正的進(jìn)行注水。

此時(shí)不光注水是動(dòng)態(tài)化的,包括組件代碼的下載都會(huì)在組件進(jìn)入視圖時(shí)才發(fā)生,真正做到了「按需加載」。

動(dòng)圖中紫色動(dòng)畫出現(xiàn),就說明漸進(jìn)式 hydrate 完成了。

對(duì)比一下全量注水和漸進(jìn)式注水的性能會(huì)發(fā)現(xiàn)首次可交互的時(shí)間被大大提前了:

當(dāng)然,我們了解原理就發(fā)現(xiàn),不光可以通過監(jiān)聽組件進(jìn)入視圖來 hydrate ,甚至可以通過 hover 、 click 等時(shí)機(jī)來觸發(fā),根據(jù)業(yè)務(wù)需求的不同而靈活調(diào)整吧。

可以訪問圖片中的網(wǎng)址獲取你喜歡的框架在這方面的相關(guān)文章:

總結(jié)

本文通過總結(jié)了 Rendering on the Web: Performance Implications of Application Architecture (Google I/O ’19) [2] 這段 Google 團(tuán)隊(duì)的精彩演講,來介紹了現(xiàn)代應(yīng)用架構(gòu)體系中的優(yōu)化手段,包括:

  • 預(yù)渲染

  • 同構(gòu)渲染

  • 流式渲染

  • 漸進(jìn)式注水

在不同的業(yè)務(wù)場景下選擇對(duì)應(yīng)的優(yōu)化手段,是一名優(yōu)秀的前端工程師必備的技能,相信看完這篇文章的你一定有所收獲。

完整 demo 地址:

https://github.com/GoogleChromeLabs/progressive-rendering-frameworks-samples

 

 

責(zé)任編輯:張燕妮 來源: 前端從進(jìn)階到入院
相關(guān)推薦

2024-11-04 16:04:06

2014-12-16 13:51:55

華為eSpace UC統(tǒng)一通信

2019-10-17 10:10:23

優(yōu)化Web前端

2021-07-22 09:00:00

SPAPWAWeb

2023-09-28 07:34:33

2010-11-15 16:20:33

Oracle系統(tǒng)優(yōu)化

2010-04-27 13:41:42

云計(jì)算

2021-07-16 06:40:19

Argo RollouAnalysis云原生

2017-11-20 17:40:52

谷歌web程序員

2022-05-09 17:33:23

PWA漸進(jìn)式Web應(yīng)用程序離線優(yōu)先

2010-07-22 08:54:14

jQuery

2021-01-13 13:49:29

漸進(jìn)式網(wǎng)頁應(yīng)用應(yīng)用程序開發(fā)

2015-02-02 15:46:59

Web應(yīng)用架構(gòu)大數(shù)據(jù)

2011-09-20 10:41:45

Web

2023-03-30 08:29:14

HTTP緩存Web應(yīng)用

2023-04-11 07:59:56

Kruise漸進(jìn)式交付

2023-01-09 17:04:24

2022-08-22 10:40:40

Kubernete部署分析運(yùn)行

2009-04-01 14:33:33

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)