新時代的 SSR 框架破局者:qwik
引言
今天這篇文章中和大家聊一聊號稱世界上第一個 O(1) 的 JavaScript SSR 框架:qwik。
別擔心,如果你不是特別瞭解 SSR 也沒關係,文章大概會從以下幾個方面作為切入點:
-
🌟 首先會圍繞對比 SSR 與 SPA 各自的優劣勢,從而展開 SSR 的執行機制以及 SSR 相較於 SPA 究竟為了解決什麼問題。
-
🌟 之後,會根據 NextJs 的執行機制思考針對目前主流 SSR 框架設計思路上存在的不足從而引出 qwik 為何會在眾多成熟框架中脫穎而出。
-
🌟 最後,筆者會針對於 qwik 提出自己的看法以及聊聊目前 qwik 存在的”問題“。
諸如社群內部 SSR 框架其實已經產生了非常優秀的作品,比如大名鼎鼎的 NextJS 以及新興勢力代表的 Remix 和 isLands 架構的 Astro、Fresh 等等優秀框架。
為何 qwik 可以在眾多老牌優秀框架中脫穎而出。接下來,讓我們一起來一探究竟吧。
SSR & CSR
目前業記憶體在非常多基於 SSR 的優秀框架,比如 Next、Remix、Nuxt 等等。
針對於 Qwik 我們先來聊聊基於 Next 體系的傳統 SSR 方案。
Client Side Rendering
在開始 SSR 之前我們先來聊聊它的對立面,所謂的 CSR(Client Side Rendering)。
伺服器端渲染 (SSR) 是一種在伺服器中進行渲染 HTML 而不是由瀏覽器中執行 JS 獲得網頁(SPA)的技術。
目前國內社群中主流框架比如 VueJs、React 等嚴格意義上來說都是基於 CSR(Client Side Rendering) 的產物。
所謂 CSR 的意味著當發出一個請求時,伺服器會返回一個空的 HTML 頁面以及對應的 JavaScript 指令碼。
比如
```html
```
當瀏覽器下載完成對應的 JS 指令碼後才會動態執行對應的 JS 指令碼然後在返回的 HTML 頁面上進行渲染頁面內容。
你可以簡單的理解為上述的 ./index.js
會在客戶端下載完成後執行該指令碼,從而執行 document.getElementById('root').innerHTML = '...'
來進行頁面渲染。
這種方式並不是從服務端下發的 HTML 檔案來進行渲染頁面,相反而是通過瀏覽器獲取到服務端下發 HTML 中的所有的 JS 檔案後執行 JS 程式碼從而在客戶端通過指令碼進行頁面渲染。
以及通常在 CSR 中當我們點選任何頁面中的導航連結並不會向服務端發起請求,而是通過下載的 JS 指令碼中的路由模組(比如 ReactRouter、VueRouter 這樣的模組)重新執行 JS 來處理頁面跳轉從而進行頁面重新渲染。
上面的概念是非常典型的 CSR ,瀏覽器僅僅接受一個用作網頁容器的 HTML 頁面,這樣的方式通常也被稱為單頁面應用 (SPA)。
優勢
那麼上述我們提到的 CSR 廣泛存在於目前大量頁面中,必然存在它自己的優勢。
在頁面初始化訪問後加載速度極快且響應非常迅速。 在頁面初始化後,網站所有的 HTML 內容都是在客戶端通過執行 JS 生成,並不需要再次請求伺服器即可重新渲染 HTML 。
此外,有關任何實時的資料獲取都可以通過 AJAX 請求對於頁面進行區域性更新從而重新整理頁面。
劣勢
可是,CSR 真的有那麼完美嗎。任何一件技術方案一定存在它的兩面性,我們來看看 CSR 方式究竟存在哪些問題:
-
初始載入時間長。首次請求完伺服器獲取到 HTML 頁面後,初始化的頁面仍然需要在一段時間內處於白屏狀態。
在初始渲染之前,瀏覽器必須等待 HTML 頁面中的所有 Javascript 指令碼載入完成並且執行完畢,此時頁面才會進行真正的渲染。
當然,使用程式碼拆分或延遲載入等多種方案可以有效的減少上述的問題。但是這些方式始終是治標不治本,因為它並沒有從本質上解決 CSR 存在的問題。
-
SEO(搜尋引擎優化) 的負面影響。
上邊我們提到過,所謂 CSR 本質上首先會返回一個空的 HTML 頁面,所以這也就造成了在搜尋引擎對於該頁面的資料爬取中會認為它是一個空頁面。從而影響對應的搜尋結果排名。
雖然說在最新的 Google 中已經可以觸發執行 JS 對於網站進行關鍵字排名,但是在 JS 體積足夠大的時候針對於 SEO 仍然是存在一部分問題導致無法解析出正確的關鍵字匹配。
當然 CSR 還存在一些其他方面的缺點,比如網站強依賴於 JS 當用戶禁用 JS 時網站只能是白屏展現給使用者等等之類。
Server Side Render
簡單聊完客戶端渲染後,我們稍微來看看所謂的服務端渲染是什麼含義。
基於舊時代的類似 Java 的 JSP 頁面我在這裡就不贅述了,顯然 JSP 的方式每個 HTML 都需要單獨請求伺服器返回對應的 HTML 內容嚴格意義上來說這也是 SSR 的方式但是很明顯這已經被時代淘汰了。
目前國內各家公司廣泛應用的服務端渲染技術大概的思路是這樣的(Next 的 SSR 模式也是同樣的思路):
當用戶首次訪問你的應用站點時:
-
首先伺服器會根據對應的 URL 在服務端根據對應路徑渲染對應的 HTML 模版。
注意這裡渲染的 HTML 模版是具有該頁面真正的內容。同時它並不具備任何互動邏輯(比如 DOM 元素的點選事件),這是一份完全的靜態站點。
-
伺服器會下發這份僅具有靜態內容的 HTML 模版,同時這份模版中也會包含對應的 JavaScript 執行指令碼。
第一時間會展示給使用者對應的 HTML 頁面,此時對於訪問站點的使用者來說**首屏渲染相較於 SPA 應用來說會非常快**。因為它並不需要在客戶端瀏覽器上再次下載和執行 JavaScript 指令碼來進行頁面渲染。 其次,針對於 SEO 的優化也會非常良好,因為伺服器上下發的 HTML 頁面是包含當前站點的真實 HTML 結構,對於搜尋引擎的爬蟲來說會非常容易的匹配到當前關鍵字。
-
之後,瀏覽器會下載當前這份 HTML 的 JS 指令碼。
因為首先呈現給使用者的一份靜態的 HTML 頁面,並不具備任何互動效果。我們需要為頁面上的元素增加對應互動,HTML 頁面中的 JS 指令碼中會包含網站的互動邏輯。
-
最後,當下載完 HTML 指令碼中的 JS 指令碼後,自然會執行這些 script 指令碼。從而發生一種被稱為 # hydrate(水合) 的方式,從而為頁面上靜態 HTML 元素再次新增對應的事件處理從而保證頁面具有互動性。
當 hydration 過程完成後,會由我們的客戶端框架接管網站的後續渲染。 在後續的導航連結跳轉和頁面渲染中和伺服器已經沒有任何關係了,我們完全可以利用客戶端的路由切換(History Api/Hash Api)利用 JS 進行頁面渲染從而保證切換頁面不用再次請求瀏覽器保證非常及時的頁面互動。
hydration
上述過程中有一個非常重要的關鍵字 hydration(水合)。
首次訪問頁面時,頁面的靜態 HTML 是在服務端生成的。 在服務端我們將生成的靜態 HTML 以及 HTML 中攜帶的 JS 指令碼傳送到客戶端。
此時靜態 HTML 會立即顯示在使用者視野中,然後瀏覽器會利用網路程序下載當前 HTML 指令碼中的 JS 指令碼。
當 JS 指令碼下載完成後,會立即執行同時發生一種被稱為 hydration 的過程。
所謂的 hydration 簡單來說,也就是客戶端下載完成 JS 指令碼後,瀏覽器會執行下載的 JS 指令碼這些指令碼中有部分內容會將已經存在的 HTML 內容通過執行下載的 JS 指令碼新增上對應的事件監聽器 從而保證頁面的互動。
注意,在 React、Vue 中 hydration 並不意味這重新渲染。因為在 Server 端已經渲染了和 Client 完全相同的 DOM 結構所以完全沒有必要在此重新渲染。
所以 hydration 的過程是給當前頁面中已經生成的 HTML 頁面新增上對應的事件監聽器。
這也是為什麼在 Next 等框架中為什麼必須要保證 Server 端和 Client 的渲染 HTML 結構必須一致的原因。
比如我們以 Next 舉例來說(Vue 也是同樣的道理):
-
當用戶訪問 www.trip.biz.com 時,服務端接收到請求呼叫 ReactDOMServer.renderToString() 生成當前頁面的 HTML 靜態結構。
-
伺服器會下發這個 HTML 頁面給客戶端,同時這個 HTML 頁面上也會攜帶一部分 JS 指令碼 script 標籤。
-
使用者的瀏覽器中會立即展現到該 HTML 頁面,同時也會下載對應 JS 指令碼並執行。
-
當 JS 指令碼執行完畢後,客戶端會呼叫 ReactDOM.hydrate() 發生水合為當前頁面的 HTML 頁面新增事件互動處理,同時後續由 JS 接管頁面的跳轉渲染。
針對於第一步 Next 中存在 Automatic Static Optimization 的優化,並不一定會在每次訪問時呼叫 renderToString 方法,有可能在構建時也會直接生成對應的 HTML 模版。
當然,在最新的 Next 版本中已經支援了Stream以及 Server Components。
整個過程就像是這張圖中的樣子:
優勢
簡單聊過了所謂 SSR 的原理後,如果你有認真看上述的內容。其實我相信相較於 CSR ,SSR 這種方式的好處不言而喻:
-
更好的搜尋引擎優化 SEO 方式,HTML 模板是從服務端直接下發這也就導致搜尋引擎爬蟲中更多的關鍵字匹配。
-
更快的首屏渲染,因為相較於 SPA 它少了在 Client 中下載和執行 JS 指令碼後渲染的過程。
-
頁面不需要 JS 也可以正常渲染,雖然沒有 JS 意味著頁面失去了可互動性。但對於禁用 JS 的使用者來說,展示一些靜態內容總比 SPA 應用的白屏來的更加友好一些對吧。
劣勢
當然,任何技術方案在不同場景下也存在它自己的不足。
-
強依賴於服務。
針對於 CSR 的方式它是一種純靜態資源。我們可以直接將它放在 CDN 上就可以良好的使用者訪問到,而 SSR 的方式必須依賴於一個伺服器進行服務端預渲染。(當然純 SSG 應用我們不在這個討論範圍之內)
同時,有服務的地方就存在併發壓力。當你需要為你的應用考慮服務端渲染的方式時,一定不要忘記為你的伺服器進行壓測。
-
Time to Interactive 可互動時間 (TTI) 的增長,雖然說 SSR 的方式有效的縮短了首屏載入的方式,但是會增加所謂的TTI(可互動時間)。
所謂的 TTI 指標測量頁面從開始載入到主要子資源完成渲染,並能夠快速、可靠地響應使用者輸入所需的時間。
因為 SSR 的方式在使用者訪問時會下發當前頁面中靜態的 HTML 內容,也就是所謂的 First Contentful Paint 首次內容繪製 (FCP) 會非常快速,但是頁面需要使用者互動效果缺又需要下載和執行完成 JS 指令碼發生 hydatrion 後才具有互動性。
這也就造成頁面的 TTI 相較於 CSR 方式會有所差勁,因為 CSR 在渲染完成後就會立即具有互動性(不需要其他任何多餘步驟)。
qwik
上述聊了那麼多前置內建,終於要和大家切入正題了。
所謂磨刀不費砍柴功,上邊和大家強調現階段 SSR 的方案以及對應的優劣勢就是為了引入下面的內容。
首先,這篇文章的目的是為了讓大家在當前眾多 SSR 框架中思考效能方面是否可以有所提升的,在伺服器方面不會過多的深入。
我們可以稍微思考下上述伺服器端渲染的過程:
第一步我們需要在服務端獲取對應頁面的 HTML 頁面,大多數情況(非純靜態頁面)就需要在服務端掉用對應渲染方法渲染出 HTML 頁面。
那麼,如果我們能在第一步渲染 HTML 頁面時,就新增對應的事件處理。後續的 3 步是不是完全可以省略下來了對吧。
其實社群內部之前已經有非常多的方案來提升所謂 SSR 框架的效能方案。
比如 Remix 的 HTTP stale-while-revalidate 快取指令
比如 astro 等新興框架的 Islands 架構方案,關於 Islands 有興趣的朋友可以參考神三元的這篇 Islands 架構原理和實踐。
針對於上面的概念,我們直接來看看 qwik 中提到的 Hydration is Pure Overhead (完全多餘的 Hydration)。
Hydration 造成的開銷
首先針對於 Hydration 的過程,我們提過到首先會在伺服器上進行一次靜態 HTML 渲染,之後當 HTML 下發到客戶端後又會再次進行 hydrate 的過程,在客戶端進行重新執行指令碼新增事件。
Hydration 過程的難點就在於我們需要知道需要什麼事件處理程式,以及將該事件處理程式附加在哪個對應的 DOM 節點上。
這個過程中,我們需要處理:
-
每一個事件處理程式中的內容,絕大多數框架中的狀態都做為閉包函式儲存在內容中。所以需要 hydration 的過程來重新獲取狀態。
-
其次,在搞清楚了每個事件處理函式的內容後。我們也需要將對應的事件處理函式附加到對應的 DOM 節點上,同時還要確保該監聽器的正確事件型別。
更加複雜每個事件處理函式中的內容是一個閉包函式,這個函式內部需要處理兩種狀態,APP_STATE 以及 FRAMEWORK_STATE。
-
APP_STATE:應用程式的狀態。簡單來說應用程式的狀態就是 HTML 事件中的各個狀態事件,如果不存在這些事件狀態那麼所有的內容都是沒有任何互動效果的。
-
FRAMEWORK_STATE:框架內部狀態。通常我們會利用諸如 React 或者 Vue 等框架進行接替渲染。如果沒有 FRAMETER_STATE,框架內部就不知道應該更新哪些DOM節點,也不知道應該在什麼時候更新它們。
通俗來說 Hydration 就是在客戶端重新執行 JS 去修復應用程式內部的 APP_STATE 以及 FRAMEWORK_STATE。
同樣還是這這張圖
在圖中的前三個階段可以被稱為 RECOVERY 階段,這三個階段主要是在重建你的應用程式。
當從 Server 端下發的 HTML 靜態頁面後,我們希望它是具有互動效果的 HTML 正常應用程式。
那麼此時 hydartion 的過程必須經歷下載 HTML 、下載所有相關 JS 指令碼、解析並且執行下載的 JS 指令碼。
RECOVERY
階段是和 hydartion 的頁面的複雜性成正比,在移動裝置上很容易花費 10 秒。
由於RECOVERY
是昂貴的部分,大多數應用程式的啟動效能都不是最佳的,尤其是在移動裝置上。
前三個階段被稱為 RECOVERY
的階段其實是完全沒有必要的,因為在服務端我們已然渲染過對應的 HTML ,但是為了應用程式的可互動性以及服務端僅保留了靜態的 HTML 模版導致不得不在 Client 上繼續執行一次 Server 端的邏輯。
總而言之,hydration 其實是通過下載並重新執行 SSR/SSG 呈現的 HTML 中的所有 JS 指令碼並執行來恢復組建中的事件處理程式。
同一個應用程式,會被髮送到客戶端兩次,一次作為 HTML,另一次作為 JavaScript。
此外,框架必須立即執行 JavaScript 以恢復在伺服器上被丟掉的 APP_STATE
和FRAMEWORK_STATE
。所有這些工作只是為了檢索伺服器已經擁有但丟棄的東西!!
比如這樣一個例子:
```tsx
export const Main = () => <>
export const Greeter = () => { return ( ) }
export const Counter = (props: { value: number }) => { const store = useStore({ count: props.number || 0 }); return ( ) } ```
上邊的例子中我們編寫了一個 Counter 的計數器元件,在傳統 SSR 過程中該元件會被渲染成為:
html
<button>Greet</button>
<button>10</button>
可以看到上邊的兩個按鈕不擁有任何處理狀態的能力。
要使網頁具有互動性,必須要做的就是通過下載對應 HTML 頁面中的 script 指令碼並執行程式碼從而恢復按鈕上的互動邏輯和狀態。
為了具有互動性,客戶端不得不執行程式碼例項化元件後重新建立狀態。
當上述過程完成後,你的應用程式才會真正具有可互動性。無疑,同一個元件的渲染邏輯被執行了兩遍,這是一個非常冗餘且耗費效能的過程。
Resumability: 更加優雅的 hydartion 替代方案
所以為了消除額外的開銷,我們需要思考如何避免重複的 RECOVERY
階段。同時還要避免上面的第四步,第四步是執行指令碼後給現有的 HTML 附加正確的事件處理程式。
qwik 中提出了一個全新的思路來規避 RECOVERY
帶來的外開銷:
-
將所有必需的資訊序列化為 HTML 的一部分。
qwik 將需要的狀態以及事件序列化儲存在 Server 端下發的 HTML 模版中,需要序列化資訊需要包括
WHAT
(事件處理函式內容),WHERE
(哪些節點需要哪些型別的事件處理函式),APP_STATE
(應用狀態), 和FRAMEWORK_STATE
(框架狀態)。 -
依賴於事件冒泡來攔截所有事件的全域性事件處理程式。
qwik 中事件處理程式是在全域性處理的,這樣我們就不必在在特定的 DOM 元素上單獨註冊所有事件。
-
qwki 內部存在一個可以延遲恢復事件處理程式的工廠函式。
該工廠函式主要用於處理 WHAT 階段,也就是用來識別某個事件處理函式中應該存在什麼指令碼邏輯。
我們可以看到所謂的 Resumable 對比 Hydration 明顯可以省略不需要後三個階段,直接獲取 HTML 後頁面其實就已經準備完畢,這無疑對於效能的提升是巨大的。
對比傳統的 hydration 方案,在客戶端獲得服務端下發的 HTML 後會立即請求需要的 JS 指令碼並執行從而為頁面附加對應的互動效果。
而 qwik 提出的概念恰恰相反,獲取完服務端下發的 HTML 頁面後所有的互動效果實際上都是一種惰性建立的效果。
因為我們在 HTML 中的每個元素中都已經通過序列化從而在它的標籤屬性上記錄了對應事件處理函式的位置以及指令碼內容(自然內容中也包含對應的狀態),所以當獲得 HTML 頁面後其實就可以說此時頁面已經載入完畢了而不需要任何實時的 JS 執行。
這樣做的好處是在 qwki 中完全可以省略 hydration 的多餘步驟,甚至可以說完全拋棄了 hydration 的概念。
客戶端完全不必和服務端的 HTML 進行水合,相同的渲染內容僅僅是在 Server 端進行一次渲染客戶端即可擁有對應的事件處理內容。
簡單來講Qwik的工作原理就是在服務端序列化 HTML 模版,從而在客戶端延遲建立事件處理程式,這也是它為什麼非常快速的原因。
qwik 工作機制
上邊我們講到了 qwik 的原理部分,同樣拿上邊的計數器的例子我們來對比下:
```tsx
export const Main = () => <>
export const Greeter = () => { return ( ) }
export const Counter = (props: { value: number }) => { const store = useStore({ count: props.number || 0 }); return ( ) } ```
在 qwik 編譯後,服務端會序列化對應元件的 HTML 結構從而下發如下的模板:
```html
```
我們可以看到經過 qwik 編譯後的 html 結構並不單單隻有 DOM 元素,同時會在對應需要狀態 & 事件的 DOM 元素上通過 HTML 元素屬性來記錄當前元素的事件和狀態資訊,這既是 qwik 中的序列化。
比如上邊 button 的 on:click
屬性記錄了該元素後續需要恢復的所有資訊。
需要注意的是序列化這一步是在服務端渲染時完成的,這也就意味著後續客戶端可以通過服務端序列化的屬性資訊進行反序列化從而達到所謂的可恢復性而不需要重複執行元件。
當然你可能會好奇 qwik 是如何進行這些事件 & 狀態的恢復,qwik 正是通過在返回的 HTML 頁面中內嵌的所謂 qwikloader 的 script 指令碼(這段指令碼的大小不超過 1kb)配合 qwikjson 對映表,從而在全域性進行恢復事件和狀態的邏輯。
正因為這個原因,使得 qwik相較於傳統 SSR 的 hydration 在 Client 中再次執行渲染從而水合頁面狀態和事件處理程式,這簡直可以說是接近零 JS 的執行過程。
最終在使用者觸發事件時候達到惰性的建立事件並執行,這個過程中完全沒有重複任何伺服器已經完成的任何工作。
整個工作過程就像下面這張圖描述的那樣:
上邊的這張圖完美的描述了 qwik 的工作原理,相信經過上述的描述大家對於這張圖中想表達的思想已經可以完美的理解了。
利用 qwik 的這個優勢,在絕大多數應用中我們可以利用 qwik 保證你的 SSR 應用在保證快速的 FCP 的前提也同樣擁有與之不相上下的 TTI 體驗效果。
惰性載入指令碼會影響使用者互動體驗嗎
當然上文說過任何框架的優勢和劣勢都不是絕對的,在筆者看來 qwik 的確會存在以下一些問題。
大多數同學看完上邊的內容我相信也會存在“惰性載入指令碼會影響使用者互動體驗嗎”這樣的疑問。
首先,qwik 中既然選擇在觸發使用者行為時,再惰性載入並執行響應的 JS 指令碼。那麼難免需要在使用者觸發互動時動態生成對應的事件處理函式進行執行。
這樣的方式相較於傳統 hydration 的確會存在一些不足,需要額外生成事件會額外造成互動響應時間的損耗而傳統 SSR 方式在頁面首次載入時就已經繫結好(相當於生成了)相應的事件處理函式。
就惰性載入生成事件這點在我看來:
針對於動態載入 JS 指令碼,其實已經存在諸如非常多的 prefetch 等等預載入技術。
無論是基於傳統 Next 方案還是基於 qwik 這種惰性可恢復的方案,利用 prefetch 等預載入技術優先在網路空閒時載入響應重要的 JS 指令碼都是非常有必要的,所以這點在我看來並不是特別重要的問題。
延遲載入會帶來 bundle 數量的上升嗎
qwik 推崇的延遲載入其實已經是一項非常成熟的構建技術了。無論是使用 webpack、rollup 又或是其他任何構建工具都存在延遲載入 & 程式碼分割的技術。
傳統構建工具中關於程式碼分割會帶來以下兩點的困難:
- 需要開發人員自行去處理更加細粒度的程式碼分割,當然這並不是最主要的。因為目前我們可以利用 Magic Comments 配合 Dynaic Imports 來解決需要手動切入多個入口點的問題。
當然,這一系列事情比起魔法註釋。因為 qwik 本身提倡的就是所謂的延遲載入,所以在框架內部已經幫我們足夠智慧的去處理這個過程。
但是需要注意的是這並不意味著開發者無法自主去控制這個過程。只是在使用框架的過程中,qwik 希望開發者更加專注於他們自身的業務邏輯。
- 當存在非常多的延遲載入時,傳統構建工具會從一個大 bundle 分割成為無數個小的 bundle 。
延遲載入模組的確會存在多個 small bundle 的問題,可是當我們擁有的 bundle 越多,其實我們就擁有更多的自由度去以各種各樣的方式去拼裝成為單個大的 bundle。
qwik 中存在足夠的方式提供給我們將多個小的 chunk 自由組合成為一個從而有效的減少細碎 chunk 的數量,當然這個點在傳統構建工具中也是這樣。
動態建立事件函式會造成記憶體洩漏嗎
qwik 的設計思想在與每次事件觸發時通過 qwikloader 來動態建立事件處理函式,相信有的同學存在疑問“那麼多次觸發事件會造成額外的開銷嗎”。
qwik 的作者 miško hevery 在 Hydration is Pure Overhead 中明確的表示過 qwik 會在每次事件執行完畢後釋放函式,相當於每次事件執行完畢都會進行一次“去水合”的過程。
所以,當你觸發一次事件和無數次事件函式在執行過程中對於記憶體佔用來說是相差無幾的。
當然相較於傳統 hydration 的方式(在頁面首次渲染時在記憶體中記錄所有狀態),無疑 qwik 這種並不在記憶體中記錄任何狀態的方式恰恰對於記憶體的佔用比 dyration 更加輕量化。
qwik 真的有那麼快嗎
說了那麼多,那麼 qwik 真的有那麼快嗎。
上圖是利用 qwik 搭建的 builder.io 官方網站,我相信 builder.io 的資料已經告訴我們答案了。
固然上述的資料並不僅僅只是單純一個 qwik 框架帶給網站的優化,一定會有程式碼層面或者構建層面等等方面的優化配合而來的資料。但是針對於 FCP 和 TTI 時間上的一致性這在一箇中型 SSR 應用程式其實可以稱得上是非常優秀了,我相信這足以說明了 qwik 的確名副其實。
結語
我們可以看出來,qwik 的核心思路還是通過更加細粒的程式碼控制配合惰性載入事件處理程式以及事件委託來縮短首屏 TTI。
文章中我們也講到了 qwik 其實並不是因為使用了多麼牛逼的演算法導致它有多麼快,而它的速度正是得益於它的設計思路,省略了傳統 SSR 下首屏需要載入龐大的 JS 進行 hydration 的過程。
當然,筆者對於 qwik 也仍是在學習階段。後續會在公司裡的更多專案嘗試 qwik 之後也會和大家分享關於它的更多心得。
總而言之,qwik 的”無水合“設計思路目前看來的確會在框架層面帶來巨大的效能提升。大家如果有機會的話也可以在專案中嘗試一下 qwik ,相信會給你帶來意想不到的收益效果。
- 新時代的 SSR 框架破局者:qwik
- 巧妙利用TypeScript模組宣告幫助你解決宣告拓展
- 從應用到原始碼-深入淺出Redux
- Blob、File、ArrayBuffer、TypedArray、DataView究竟應該如何應用
- 如何進階TypeScript功底?一文帶你理解TS中各種高階語法
- 為什麼Proxy一定要配合Reflect使用?
- 一年三跳,來聊聊我的想法
- Webapck5核心打包原理全流程解析
- 詳解「react-dom」 API
- 「Babel」為你的元件庫定製化一款Tree-Shaking外掛吧
- 「前端基建」帶你在Babel的世界中暢遊
- 全方位解析瀏覽器渲染原理
- 一次useEffect引發瀏覽器執行機制的思考
- 十分鐘帶你手撕一份"漸進式"JS深拷貝
- React-Webpack5-TypeScript打造工程化多頁面應用
- Webpack中各種環境變數的正確姿勢