Slide 1

Slide 1 text

Webレンダリング最適化の道 ~ React、Next.js を中心に ~ レンダリングシリーズ(2)

Slide 2

Slide 2 text

職業 自己紹介 韓国人です。大学時代にはげしくアニメ オタクをやってて日本語が上手くなりま した。 フロントエンドエンジニア(2020/11 ~) React、Typescript メインでやってます

Slide 3

Slide 3 text

CONTENTS 1. Next.js SSR の限界 2. Code Splitting 3. React Server Component 4. Streaming & Selective Hydration 5. まとめ

Slide 4

Slide 4 text

1. Next.js SSR の限界

Slide 5

Slide 5 text

2016年、Next.js により React でも SSR が可能になる

Slide 6

Slide 6 text

Next.js の SSR プロセス ⑧ HTML表示 ⑧ インタラクティブになる ⑨JS実行

Slide 7

Slide 7 text

Next.js の SSR プロセス ⑧ HTML表示 ⑧ インタラクティブになる ⑨JS実行 データが全部フェッチされるまでまってから、、 HTML作る ← インタラクティブではない Hydration

Slide 8

Slide 8 text

Next.js の SSR プロセス ブラウザが最初の1バイトの レスポンスを受け取るまでの時間 ブラウザが最初の視覚的コンテンツを 画面に描画したタイミング ユーザーが動的な操作ができる ようになるタイミング

Slide 9

Slide 9 text

SSR の限界 1. すべてのデータフェッチが終わってからHTMLがつくれる

Slide 10

Slide 10 text

SSR の限界 2. すべてのコードがロードされてから hydration できる

Slide 11

Slide 11 text

SSR の限界 *前のステップが終わるまで次のステップが始まらない

Slide 12

Slide 12 text

🤔

Slide 13

Slide 13 text

2. Code Splitting

Slide 14

Slide 14 text

Suspense & React.lazy(2018年) React.lazy() によるクライアントサイドの Code Splitting ができるようになり、 Suspense により読み込み中の UI を制御できるようになった。

Slide 15

Slide 15 text

Suspense & React.lazy(2018年) React.lazy() によるクライアントサイドの Code Splitting ができるようになり、 Suspense により読み込み中の UI を制御できるようになった。 アプリケーションのコードを1つのファイルにまとめるのではなく、 複数のファイルに分割すること 例)ファストビューで見えるところだけ先に読み込んで、ほかは後で読み込む JS JS JS JS ↑Code Splitting あり ↑Code Splitting なし

Slide 16

Slide 16 text

Suspense & React.lazy(2018年) React.lazy() によるクライアントサイドの Code Splitting ができるようになり、 Suspense により読み込み中の UI を制御できるようになった。 JavaScript を複数のファイルに分割する = UIのロード速度がそれぞれ違う

Slide 17

Slide 17 text

Suspense & React.lazy(2018年) React.lazy() によるクライアントサイドの Code Splitting ができるようになり、 Suspense により読み込み中の UI を制御できるようになった。 ロード未完了のUIが完了するまで、 ローディングUIを出すことができる ↑ローディングUIあり ↑ローディングUIあり

Slide 18

Slide 18 text

Suspense & React.lazy(2018年) 優先度低いUIは読み込みを遅らせて、その間ローディングUIを出すことができる (ちなみに React.lazy と Suspense は必ず一緒に使わないといけない) 早いロードが必要な部分のJSバンドルサイズを 減らせるので、ここらへんの時間を多少短縮できる

Slide 19

Slide 19 text

Suspense & React.lazy の限界 登場時は、クライアントのコードしか分割することしかできない SSRには非対応だったので、SSRの限界を改善できない

Slide 20

Slide 20 text

next/dynamic(2019年) SSRにも対応した Code Splitting 機能 ロードを遅延させるところのSSRをオフにすることができる JS JS JS ここは初期ロードに見えないから SSRも不要!オフにしちゃおう!とかができる (= HTML 作る時間減らせる)

Slide 21

Slide 21 text

next/dynamic(2019年) SSRにも対応した Code Splitting 機能 ロードを遅延させるところのSSRをオフにすることができる SSRオフにしたらここらへんの 時間も多少短縮できる 早いロードが必要な部分のJSバンドルサイズを 減らせるので、ここらへんの時間を多少短縮できる

Slide 22

Slide 22 text

next/dynamic(2019年) SSRにも対応した Code Splitting 機能 ロードを遅延させるところのSSRをオフにすることができる SSRオフにしたらここらへんの 時間も多少短縮できる 早いロードが必要な部分のJSバンドルサイズを 減らせるので、ここらへんの時間を多少短縮できる すべてのステップが同期的であることは 変わってないので、ドラマチックな効果はない

Slide 23

Slide 23 text

ただこの後、もっとすごいのがくる

Slide 24

Slide 24 text

3. React Server Component

Slide 25

Slide 25 text

まず従来の React でデータフェッチしてみよう

Slide 26

Slide 26 text

① React と JSバンドルを送る ② JS ダウンロード、実行 ③ React 起動 ④ useEffect 実行 ⑤ fetchProducts() を実行 ⑥ state にセット → 再レンダリング ⑦ HTML 描画 まず従来の React でデータフェッチしてみよう

Slide 27

Slide 27 text

React Server Component (2020) React コンポーネントをサーバー上で実行して、その出力結果だけをクライアントに送る Server Component だとこういうことができる

Slide 28

Slide 28 text

React Server Component (2020) React コンポーネントをサーバー上で実行して、その出力結果だけをクライアントに送る Server Component だとこういうことができる React Component が async 関数! useEffect なしの Side effect!

Slide 29

Slide 29 text

React Server Component (2020) React コンポーネントをサーバー上で実行して、その出力結果だけをクライアントに送る Server Component だとこういうことができる Server Componentは クライアント 側の操作で再レンダリングされない *コンポーネントが1回だけ実行される

Slide 30

Slide 30 text

Server Component の動き ① fetchProducts 実行(レスポンスは↑だとしよう) ② JSXは結果的に↓になるはず

Slide 31

Slide 31 text

Server Component の動き ③ クライアントで描画する情報を結果値として出力 イメージ的にこんな感じ(JSONっぽい構造にシリアライズされる) ④ この描画情報をクライアントに送る ⑤ クライアントのReact がこれを見てDOMつくる

Slide 32

Slide 32 text

Server Component の動き ① fetchProducts() を実行 ② 取得したデータをもとに描画情報生成 ③ React と描画情報を送る ② React 起動 ③ React が DOM を構築 ④ HTML 描画

Slide 33

Slide 33 text

Server Component の動き ① fetchProducts() を実行 ② 取得したデータをもとに描画情報生成 ③ React と描画情報を送る ② React 起動 ③ React が DOM を構築 ④ HTML 描画 Server Component が実行される クライアントに来てからは 変わることない コンポーネントそのものが クライアントに行くことはない

Slide 34

Slide 34 text

Server Component の良さ ➔ JavaScript バンドルサイズが減る ➔ FEアプリケーションにも機密情報が置けるようになる

Slide 35

Slide 35 text

Server Component の良さ ➔ JavaScript バンドルサイズが減る ➔ FEアプリケーションにも機密情報が置けるようになる コード量が多くても(特にライブラリー使ってる場合)JSバンドルが増えない

Slide 36

Slide 36 text

Server Component の良さ ➔ JavaScript バンドルサイズが減る ➔ FEアプリケーションにも機密情報が置けるようになる = クライアントでやったらまずいこともできる(DB に直接アクセスするとか) ↓こういうことできる

Slide 37

Slide 37 text

+) Server-Side Rendering vs Server Component ➔ Server-Side Rendering ➔ Server Component ● 目的 → HTML を事前に生成して SEO や初期表示を改善 ● できること → 事前にHTMLを生成してクライアントに送る ● できないこと → JavaScript バンドルサイズを減らす ● 目的 → クライアントに送る JS を最小化してパフォーマンスを改善 ● できること → JavaScript バンドルサイズを減らす ● できないこと → 事前にHTMLを生成してクライアントに送る RSCの出力結果は描画するに必要な情報であってHTMLそのものではない! 実際HTMLを生成するのはクライアント側のReact(JavaScript)

Slide 38

Slide 38 text

+) Server-Side Rendering vs Server Component ➔ Server-Side Rendering ➔ Server Component ● 目的 → HTML を事前に生成して SEO や初期表示を改善 ● できること → 事前にHTMLを生成してクライアントに送る ● できないこと → JavaScript バンドルサイズを減らす ● 目的 → クライアントに送る JS を最小化してパフォーマンスを改善 ● できること → JavaScript バンドルサイズを減らす ● できないこと → 事前にHTMLを生成してクライアントに送る RSCの出力結果は描画するに必要な情報であってHTMLそのものではない! 実際HTMLを生成するのはクライアント側のReact(JavaScript) つまり Server-side Rendering と Server Component 一緒に使ったら 事前にHTML生成もできるしJavaScript バンドルサイズも減らせていい感じ

Slide 39

Slide 39 text

Next.js 13.4 からの大きな変化 2023年5月、App Router が Stableとして正式リリース! App Router からは React Server Components がデフォルトになる = データフェッチ方法に大きな変化が現れる

Slide 40

Slide 40 text

Page Router の場合 Data Fetch 1 Data Fetch 2 Data Fetch 3 Data Fetch 4 getServerSideProps Client Component Client Component Client Component Client Component Client Component Client Component Client Component

Slide 41

Slide 41 text

App Router の場合 Data Fetch 1 Data Fetch 2 server component Server Component Data Fetch 4 Data Fetch 3 Server Component Server Component Client Component Client Component Server Component Client Component コンポーネントごとのデータフェッチが可能になる

Slide 42

Slide 42 text

そして更に改善を目指して新しい技術が登場

Slide 43

Slide 43 text

4. Streaming & Selective Hydration

Slide 44

Slide 44 text

SSRの限界その1 1. すべてのデータフェッチが終わってからHTMLがつくれる この課題を改善する

Slide 45

Slide 45 text

Suspense & React.lazy を思い出してみよう JS JS JS React.lazy Suspense 2018年の Suspense は lazy だけ対応していたが、ゴールはサーバー側も対応すること! そしてそれが現実になり、SSRの限界を解決しにかかる Suspense が強くなる

Slide 46

Slide 46 text

Streaming HTMLを完成を待たずに部分的にクライアントへ送信できる仕組み 従来のSSR Streaming

Slide 47

Slide 47 text

Streaming 従来のSSR Streaming これだけみると lazy + Suspense と同じように見えるかもしれないが、 大事なことは Streaming は Server Component と一緒に使われることだ! HTMLを完成を待たずに部分的にクライアントへ送信できる仕組み

Slide 48

Slide 48 text

Suspense & React.lazy Data Fetch 1 Data Fetch 2 Data Fetch 3 Data Fetch 4 getServerSideProps Client Component Client Component Client Component Client Component Client Component Client Component Client Component できること = ここらへんののJSバンドルは 分割して後で読み込もう! できないこと = このデータフェッチを分割して 後で実行しよう!

Slide 49

Slide 49 text

Streaming Data Fetch 1 Data Fetch 2 server component Server Component Data Fetch 4 Data Fetch 3 Server Component Server Component Client Component Client Component Server Component Client Component できること = データフェッチもHTML生成も非同期でする!

Slide 50

Slide 50 text

Streaming Data Fetch 1 Data Fetch 2 server component Server Component Data Fetch 4 Data Fetch 3 Server Component Server Component Client Component Client Component Server Component Client Component できること = データフェッチもHTML生成も非同期でする! 非同期データフェッチ・HTML生成 (他のコンポーネントがこれを待たない) 非同期データフェッチ ・HTML生成 (他のコンポーネントが これを待たない)

Slide 51

Slide 51 text

Streaming のメリット データフェッチ・HTML生成が非同期でできる データフェッチいらないコンポーネントは早く表示しちょう データフェッチ全部待ってると遅すぎるから 2つに分割して非同期にしよう

Slide 52

Slide 52 text

SSRの限界その2 この課題も改善する 2. すべてのコードがロードされてから hydration できる

Slide 53

Slide 53 text

Selective Hydration 一部のコンポーネントだけを、優先的にハイドレーションする仕組み Selective Hydration 従来のSSR

Slide 54

Slide 54 text

Selective Hydration 従来のSSR Selective Hydration 一部のコンポーネントだけを、優先的にハイドレーションする仕組み Suspense で遅らせたコンポーネントを待たずに 優先順位高いコンポーネントを先に Hydration する

Slide 55

Slide 55 text

こうやってレンダリング最適化の工夫はたえないのだ 従来のSSR Streaming & Selective Hydration

Slide 56

Slide 56 text

5. まとめ

Slide 57

Slide 57 text

● SSRの限界 ○ データフェッチがすべて終わってからHTML作成ができる ○ すべてのJavaScriptがロードされてから Hydration できる ● Code Splitting ○ React.lazy + Suspense → クライアントのコードを分割できる ○ next/dynamic → SSRの制御 + コード分割ができる ● React Server Component ○ サーバー上で実行され、その出力結果だけをクライアントに送る ○ JSバンドルサイズを減らしパフォーマンス改善ができる ● Streaming & Selective Hydration ○ データフェッチ、HTML生成、Hydration を非同期でできる

Slide 58

Slide 58 text

それでも内容多くてすみません! でも私がここ3年間キャッチアップしてきたものを皆さんは1時間で キャッチアップできるようになったので許してください! 最後に レンダリングの話はこれ以外にもたくさん…本当にたくさんありますが、 今回は私が知ってほしい部分を中点的に説明させていただきました。 これからレンダリング戦いがどうなっていくかがとても興味深いです。

Slide 59

Slide 59 text

ご清聴ありがとうございました

Slide 60

Slide 60 text

- nextjs.org, 「Loading UI and Streaming」, https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming - github.com, 「New Suspense SSR Architecture in React 18」, https://github.com/reactwg/react-18/discussions/37 - joshwcomeau, 「Making Sense of React Server Components」, https://www.joshwcomeau.com/react/server-components/ - plasmic, 「How React server components work: an in-depth guide」, https://www.plasmic.app/blog/how-react-server-components-work 参考資料