--- tags: FrontEnd --- # How Morden App Render > CSR is Dead 這句話並不是跨大其詞,在 2022 年,前端推陳出新了許多 meta framework,例如 - [next.js](https://nextjs.org/docs/getting-started) - [svelte kit](https://kit.svelte.dev/) - [qwik.js](https://qwik.builder.io/) - [solid.js](https://www.solidjs.com/tutorial/introduction_basics) - [astro.js](https://docs.astro.build/en/getting-started/) - [deno.js](https://deno.land/manual@v1.30.3/introduction) > meta framework 就是泛指在 framework 之上,還解決了 routing, SEO, data-fetching 相關問題的 framework,詳情可以看[這篇文章](https://hackmd.io/@ChrisW/r19iRgaho), > 為了容易理解,這邊先簡單的將 SSR 與 Meta Framework 劃上等號 這些新框架解決了大部分 SSR 之前會被詬病的問題,並繼承了 SSR 以往的優點: - 第一頁 rendr 快速 (FCP) - 具有 SEO - 流暢的互動(不會有瞬間白畫面) - clien 負擔較小 以上總總原因讓 SSR 逐漸與 CSR 可以站在同一個跑道競爭,而其中大功臣就不得不提到 Hydration 這個步驟。 ## Hydration ![](https://i.imgur.com/V9Y8ugJ.png) > 來自 [Qwik Doc: introducing-resumability](https://qwik.builder.io/docs/concepts/resumable/#introducing-resumability) Hydration 最主要的動作就是在 server 生成好的 HTML 上面加入 event handler,這樣才能在互動之後,做出對應的改變,步驟是這樣的: 1. 在 server 產生 HTML,並下載 2. 拿取這個頁面需要的 JS file 3. 在 client 組裝 HTML 和 JS,後續的 render 也繼續在 client 進行 也是因此,現代的 SSR 才能兼顧 FCP、SEO 和流暢的使用者體驗 但因為在第一次需要下載大量資源,又會造成另一個問題,那就是 TTI 過長。 > TTI (time to interactive,使用者可以跟頁面互動的時間點) 而為了解決 Hydration 帶來的問題,有很多框架都給出了自己的答案。 ## Solation 1: Qwik - Resumability ![](https://i.imgur.com/cO4zbLS.png) > 來自 [Qwik Doc: introducing-resumability](https://qwik.builder.io/docs/concepts/resumable/#introducing-resumability) >Resumability is about pausing execution in the server and resuming execution in the client without having to replay and download all of the application logic. 接續性( Resumability ) 的意思是: Qwik 可以在 server 暫停執行後,直接在 client 接續執行,但不需要下載任何 js file 或執行任何 APP 邏輯,包含掛載 listener、加載狀態 (aka hydration)。 Qwik 之所以可以做到這點最大的原因在於序列化( serialize )。 ### Serialize 序列化(Serialize)在使用 hydration 技術的 SSR 框架中也時常被使用到 作用在於將裝態包裝成 json 或是任何一個可恢復 / 可取用的格式,鑲入在 ``<script>`` 中,這樣可以幫助 client side 更快的 render。 ![](https://i.imgur.com/9W2lAZO.png) > 來自: [rendering on the web](https://web.dev/rendering-on-the-web/#a-rehydration-problem:-one-app-for-the-price-of-two) 而 Qwik 將序列化作用在三個地方 - listeners - component tree - application state Qwik 會將序列化後的 eventhandler 掛在 dom 元件的 attritube 上。 ``` <button on:click="./chunk.js#handler_symbol">click me</button> ``` 這個元件被觸發,qwik 就只會下載這個按鈕相關的 js file。 當狀態改變時,Qwik 會在 SSR 就對 DOM 的狀態做序列化,這樣等下次需要時,就能夠快速用這個序列化資料生成 HTML 如此一來就能達到在 client side 0 hydration 的效果了。 但需要注意的是,要使用 Qwik 的這種方式,可能會跟傳統的 parent-child 撰寫方式會有很大的不同。 每個 component 之間必須要不能互相依賴,或是都可以採用 Dependency inject 的方式。 延伸閱讀:[Qwik doc](https://qwik.builder.io/docs/concepts/resumable/#introducing-resumability)、[保哥原理介紹](https://www.youtube.com/watch?v=bU6KeelyRfc&t=3105s) ## Solation 2: Astro - Island Architecture ![](https://i.imgur.com/dut24Tm.png) Islands Architecture 最早是在 2019 由 Katie Sylor-Miller 提出的,並由 Preact 的創建者 Jason Miller 透過[文章](https://jasonformat.com/islands-architecture/)擴展。 原理是把每個需要用到JS的元件獨立出來,變得像是獨立的插件一樣可以放到你靜態網站的任何一個地方。 而要讓這些 component 之間的 render 進程不受影響,就必須要提到 Partial Hydration。 > Partial Hydration 是針對部分的 component 做出 hydration,只對需要 render 的部分發送請求並下載 js file ![](https://i.imgur.com/fl7n8ii.png) 套用到上面的 Islands Architecture 來說,每個 component 都有自己的 Hydration ,每一個 Hydration render 獨立於頁面上的任何其他 render,也因此才能做到彼此不會互相影響。 Astro 在預設情況下可以達到 0 js file,更可以在任何一個 astro 頁面下使用 React, Preact, Svelte, Vue, SolidJS, AlpineJS 等 ui library ### How it's work in Astro 接著我們來看一下 astro 實際上是怎麼運作的 Astro 將 元件分成 - 動態交互元件 - 靜態元件 但既然同時存在動態與靜態元件那server side生成的時候該怎麼去做分別呢 先看看以下的 astro 搭配 react 的範例: ``` // src/pages/index.astro --- // Example: Use a dynamic React component on the page. import MyReactComponent from '../components/MyReactComponent.jsx'; --- <!-- This component is now interactive on the page! The rest of your website remains static and zero JS. --> <MyReactComponent client:load /> ``` 在 component 元件上的 attribute `client:load` 是[client 指令](https://docs.astro.build/en/reference/directives-reference/#client-directives),告知 astro 該 componey 使用動態還是靜態元件呈現。 同時,在實作Islands Architecture的框架會透過『占位符』(placeholders/slots)的方式預先留好插入動態元件的地方接著在clien-side各別同時做partial hydration。 ### 關於 astro 小結 總結 Island Architecture其實就是結合不同的前端技術所建構出來的設計模式(SSR + SSG + Partial Hydration + slot/placeholder) 1. 繼承大多數 SSR + SSG 的優點,減少非常多無謂 js 載入,也解決的 TTI 過高的主要問題。 2. 如果是 component 之間需要大量的互動,那有關於獨立 component 彼此之間交流就會是一大問題。 3. 還是適用於以靜態內容為主的網站。 ## 其他 render 方式 其實還有其他解決 TTI 的方式,但礙於篇幅就先稍微點到一下就好 - [streaming](https://www.patterns.dev/posts/ssr/) - [SSG](https://www.patterns.dev/posts/incremental-static-rendering/) streaming 指的是一邊產生許多小片段的 HTML,一邊回傳給使用者,瀏覽器可以更早收到 HTML 的小片段,更早開始執行瀏覽器的渲染工作。這可以改善 TTFB, FCP。 SSG 就是指 static site generate: - pre-render - 所有已知的 page 都被 render ( no dynamic router) - server cost 超大 ## REF https://www.quora.com/Why-is-React-a-library-and-not-a-framework https://www.ombulabs.com/blog/javascript/what-is-a-javascript-meta-framework.html https://kayshih.com/posts/islands-architecture https://docs.astro.build/en/concepts/islands/#what-is-an-astro-island https://www.youtube.com/watch?v=bU6KeelyRfc&t=2640s https://www.patterns.dev/posts/#design-patterns https://www.youtube.com/watch?v=cLivMMSeFgE https://www.builder.io/blog/hydration-is-pure-overhead https://qwik.builder.io/docs/concepts/resumable/#resumable-vs-hydration https://shubo.io/rendering-patterns/#ssr-with-hydration https://dev.to/this-is-learning/why-efficient-hydration-in-javascript-frameworks-is-so-challenging-1ca3 https://www.youtube.com/watch?v=x2eF3YLiNhY