Slide 1

Slide 1 text

@yusuke_blog1026 2023/10/26 Next.jsから見る Webフロントエンドの歴史

Slide 2

Slide 2 text

@yusuke_blog1026 Yusuke Inai Work 🏢 Counterworks Inc. 👨‍💻 Frontend Developer Private 🧲 Keiba

Slide 3

Slide 3 text

🎯 目的 現代フロントエンドの大枠の流れの理解 Next.js や React が何を解決しようとしているのか? の理解 フロントエンドという領域そのものについての理解 (「JSON色付け係」からの脱却)

Slide 4

Slide 4 text

✅ アジェンダ  JavaScript の発展と Ajax  SPAの流行と各 FW の乱立  React の登場と宣言的 UI  Next.js の登場と Pre-rendering  React Server Components と App Router 1. 2. 3. 4. 5.

Slide 5

Slide 5 text

🙅‍♂️ 話さないこと React / Next.js の各機能についての説明 Hooks, Rendering, Managing State, Suspense Routing, Cache, Server Actions 3rd Party ライブラリやツール等の使い方 UI ライブラリ, テスト, Storybook, etc. ハンズオン

Slide 6

Slide 6 text

✅ アジェンダ  JavaScript の発展と Ajax  SPAの流行と各 FW の乱立  React の登場と宣言的 UI  Next.js の登場と Pre-rendering  React Server Components と App Router 1. 2. 3. 4. 5.

Slide 7

Slide 7 text

HTML APサーバ ブラウザ いわゆるMPA(クラシックSSR) DB

Slide 8

Slide 8 text

※クラシックSSR SSR = サーバーサイドレンダリング リンクをクリックするたびにサーバーサイドで HTMLをレンダリングする 後述するSSRと区別 生成されるページ自体は静的

Slide 9

Slide 9 text

HTML M C V
<%= @user.name %>
HTML = Template (data) Web MVCフレームワーク APサーバ ブラウザ DB data テンプレート

Slide 10

Slide 10 text

Web MVCフレームワーク サーバーサイドでは MVC を利用 Ruby on Rails, Laravel, Spring, etc. View には「テンプレート」を利用 例: ERB, Blade, JSP, etc. HTML = Template (data) リクエスト毎にページ全体をレンダリングして返す

Slide 11

Slide 11 text

ブラウザ間で言語仕様が 異なり、実装者は大変 JavaScript不遇期 Web でも良質な UX が求 められ始める! インタラクティ ブなコンテンツ の普及 JavaScriptの歴史 JavaScript台頭 Netscape Navigator と IE で 実装された ECMAScript / Flash ブラウザ間の仕様を統一する ため、ECMAScriptが策定。 Flashも登場 Ajax / HTML5 Google Map により Ajax が 注目される。またHTML5 に より API が充実 iPhone / Android ネイティブアプリの普及

Slide 12

Slide 12 text

JavaScriptの台頭 1995年 Netscape Navigator 2.0 に実装 翌年にはInternet Explorer 3.0 にも搭載され、急速に 普及 ECMAScriptの誕生 ブラウザ間での言語仕様を統一 開発者にとって使い勝手の良いものへ

Slide 13

Slide 13 text

Ajax の登場 過剰装飾で、使い勝手の悪い Web ページが量産 JS 起因のクラッシュや悪用ウイルスも流行 JS をオフにする人も... 2005 年 Ajax 登場 Google Map 革命 HTML5 により APIが強化され、機能が充実 JavaScript 復権

Slide 14

Slide 14 text

HTML M C V APサーバ ブラウザ DB MPA(クラシックSSR + jQuery) インタラクション Webサーバ DOM イベントハンドラ 登録 イベント DOMを操作 JS

Slide 15

Slide 15 text

MPA(クラシックSSR + jQuery) クラシック SSR が生成した HTML と連携する JS を あとづけ コンテンツ、スタイル、ロジックを分離するのがベストプラク ティスだった 2006 年 jQuery 「write less, do more」 ブラウザ間の差異を吸収

Slide 16

Slide 16 text

MPA(クラシックSSR + jQuery)の課題: アプリケーションの構造

Slide 17

Slide 17 text

MPA(クラシックSSR + jQuery)の課題: アプリケーションの構造 命令的なイベントハンドラ DOMを参照→処理を行う→DOMを更新 「変化の過程」をひとつずつ記述 大量のイベントハンドラが散らばる DOM更新処理も散らばる DOMが巨大でグローバル変数になってしまう 破壊も容易

Slide 18

Slide 18 text

HTML M C V DOM JS テンプレート フロントエンド側で 開発するリソース サーバーサイド側で 開発するリソース 開発リソースではない MPA(クラシックSSR + jQuery)の課題: ワークフロー APサーバ ブラウザ DB Webサーバ 依 存 齟齬 スケジュール 二重開発

Slide 19

Slide 19 text

MPA(クラシックSSR + jQuery)の課題: ワークフロー 「HTML」はリソースではない リソースは「テンプレート」 JS の処理対象となるのは AP サーバの実行結果 JS 側が必要とする HTML の修正は誰がするのか? テンプレートの修正。サーバーサイド側? 開発サイクルが異なる 二重開発 サーバーサイドのテンプレートとフロントの JS が機能的に重複

Slide 20

Slide 20 text

✅ アジェンダ  JavaScript の発展と Ajax  SPAの流行と各 FW の乱立  React の登場と宣言的 UI  Next.js の登場と Pre-rendering  React Server Components と App Router 1. 2. 3. 4. 5.

Slide 21

Slide 21 text

HTML DOM JS SPA (Single Page Application) サーバ ブラウザ フォームをサブミット イベント DOM更新 イベント DOM更新 JSON JSON リンクをクリック ブクマ等から ロードされる HTML ページは1つ (Documentオブジェクトは不変)

Slide 22

Slide 22 text

SPA (Single Page Application) Single Page Application ナビゲーションの画面遷移もブラウザ上の JS でレンダリング 単一の HTML ページ(あるいは Document)だけで構成 ナビゲーションの単位も「ページ」と呼ばれる 例)トップページ、一覧ページ、商品ページ、etc.

Slide 23

Slide 23 text

静的 HTML DOM JS SPA の起動シーケンス Web サーバ ブラウザ レンダリング インタラクション JSON API サーバ JSON DB (CSR) イベント レンダリング (CSR) コンテンツとしては空 (SSR 不要)

Slide 24

Slide 24 text

SPA の起動シーケンス 基本的には CSR (Client-Side Rendering)のみ クライアントサイド(ブラウザ)だけでコンテンツをレンダリ ング サーバサイドでは HTML ページを動的にレンダリングしない ここでの「レンダリング」とは、単純な HTML 生成処理 広義の「レンダリング」 react.dev でいうところの “Rendering” とは違う

Slide 25

Slide 25 text

フロントエンドの概念的な話 UI ユーザー システム

Slide 26

Slide 26 text

State View Action

Slide 27

Slide 27 text

フロントエンドと状態 クライアントサイドで状態を扱うことが増える ステートレス→ステートフル 画面の表示 → ユーザのアクション → 状態の変化 → 画面の表 示... 状態の例(保持されていて変化・変更するもの) TODOリストのデータ(Model State) モーダルの開閉状態(UI State) どのページを開いているか?(Location State) 誰がログインしているか?(Auth State) 状態は「スコープ」と「ライフタイム」を持つ

Slide 28

Slide 28 text

MV* フレームワークと状態 クラシックSSR + jQuery DOM 状態 イベントハンドラ 参照 更新 イベント MV* フレームワーク DOM Controller イベント View 更新 Model 状態 更新要求 変更通知

Slide 29

Slide 29 text

MV* フレームワーク MV* フレームワークの登場 例: Backbone.js, Angular JS, etc. 10年代半ば以降は MV* フレームワークではない React と VueJS が主流になった MPA(クラシックSSR + jQuery)の問題を解決 フロントエンドのアプリケーションに構造が付与 https://speakerdeck.com/nrslib/how-to-make-front-end DOM のグローバル定数化・イベントハンドラの散らばり↓ サーバサイドのテンプレートが不要に サーバサイドと疎結合に。HTML / CSS / JS がフロント側で完結

Slide 30

Slide 30 text

MV* フレームワークの課題 Angular JS は双方向バインディングの実装アルゴリズ ムに難あり 監視する値が増えるとパフォーマンスが落ちる View と Model の同期 手続き的な処理が残っている...

Slide 31

Slide 31 text

✅ アジェンダ  JavaScript の発展と Ajax  SPAの流行と各 FW の乱立  React の登場と宣言的 UI  Next.js の登場と Pre-rendering  React ServerComponents と App Router 1. 2. 3. 4. 5.

Slide 32

Slide 32 text

React The library for web and native user interfaces Web とネイティブユーザインターフェースのためのライブラリ 開発元は Meta (旧 Facebook) フロントエンド界の(現)覇者 語りたいことは色々あるが、結局は以下の 2 つに集約 される コンポーネント指向 宣言的 UI ⇔ UI = f (state)

Slide 33

Slide 33 text

コンポーネント指向 MV* DOM Controller イベント View コンテンツ Model 状態 更新要求 変更通知 React DOM ロジック ロジック 状態 コンテンツ Component ロジック 状態 コンテンツ Component ロジック 状態 コンテンツ Component 更新 更新 子コンポーネント (複数可)

Slide 34

Slide 34 text

コンポーネント指向 React の構成要素はコンポーネント Model, Controllerなどは不要。 View と Modelの同期も気にする必要ない コンポーネントが持つもの ロジック、イベントハンドラ 状態 コンテンツ(JSX) コンポーネントは親子関係を持つ 各部分を担当するコンポーネントにわける メンテナンス性や再利用性↑

Slide 35

Slide 35 text

   ... 宣言的 UI ⇔ UI = f (state) DOM(UI) 状態 f (レンダリング)    ... 状態 f (レンダリング) ユーザー操作による変化 ・・・ DOM(UI)

Slide 36

Slide 36 text

宣言的 UI ⇔ UI = f (state) React のメンタルモデル 「変化の過程」ではなく「結果」を記述(=宣言的) 毎回新規にレンダリングするのに近い。面倒な DOM 更新は React 側が担う 「状態」と「UI」を明確に分離 →「状態」が SSoT UI = f (state)。UI は現在の状態の映写機 クラシック SSR と類似 リクエストごとにテンプレート実行 → HTML 生成 HTML = Template (data) 面倒な DOM 更新については、jQueryのお仕事 これを自力でやろうとするからつらみ...

Slide 37

Slide 37 text

③状態が更新 仮想 DOM A B C
“Foo” “Bar”
“Foo” “Bar” コンポーネント 仮想DOM 実DOM A B C
“Baz” “Bar”
“Baz” “Bar” 最初の レンダリング 再レンダリング ①実行 ②反映 ④実行 ⑥差分反映 ⑤比較

Slide 38

Slide 38 text

③状態が更新 仮想 DOM A B C
“Foo” “Bar”
“Foo” “Bar” コンポーネント 仮想DOM 実DOM A B C
“Baz” “Bar”
“Baz” “Bar” 最初の レンダリング 再レンダリング ①実行 ②反映 ④実行 ⑥差分反映 ⑤比較 レンダー コミット 広義のレンダリング

Slide 39

Slide 39 text

仮想 DOM React のメンタルモデル 「変化の過程」ではなく「結果」を記述(=宣言的) 毎回新規にレンダリングするのに近い。面倒な DOM 更新は React 側が担う 毎回全更新だと遅い そこで登場するのが「仮想 DOM」 DOM の代わりに軽量なJSオブジェクトで仮想 DOM 構築 VDOM = f (state) 差分更新(Reconciliation) 前回レンダリング時と新しい仮想 DOM を比較 → 実DOM へ反映

Slide 40

Slide 40 text

※仮想 DOMの補足 HTML =
DOM API = document.createElement("div") 仮想 DOM 構造体 = React.createElement("div") 仮想 DOM アルゴリズムの実行 =ReactDOM.render(, el) 前後の 2 つの木構造の比較することで、その差分を埋める操作を 自動的に導出できるはず、というのがスタート地点

Slide 41

Slide 41 text

✅ アジェンダ  JavaScript の発展と Ajax  SPAの流行と各 FW の乱立  React の登場と宣言的 UI  Next.js の登場と Pre-rendering  React ServerComponents と App Router 1. 2. 3. 4. 5.

Slide 42

Slide 42 text

静的 HTML JS エンジン SPA の課題①:クローラー クローラー Web サーバ コンテンツとしては空 コンテンツがないから インデックスできない JS に対応した クローラー インデックスが遅れて しまう!

Slide 43

Slide 43 text

静的 HTML DOM JS SPA の課題②:初期表示 Web サーバ ブラウザ レンダリング JSON API サーバ DB (CSR) コンテンツとしては空 (SSR 不要) コンテンツは 空 コンテンツは 存在 LCP が遅 い!

Slide 44

Slide 44 text

※ Core Web Vitals 2.5 s 4 s 100 ms 300 ms 0.1 0.25 初期画面のうち、最大の領 域を占めるコンテンツが表 示されるまでの時間 Large Contentful Painting First Input Delay 初回のユーザー操作・入力 が可能になるまでの時間 視覚的な安定性を計測する ためのレイアウトシフト数 (いわゆるガタツキ) Cumlative Layout Shift LCP FID CLS

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

SPA の課題 MPA (クラシック SSR)からの移行コスト SEO 対策が困難 10 年代半ばのクローラーは JS非サポート 現在は Google など JS サポートクローラーあるが... インデックス遅い OGP 対応困難・通信環境に依存 初期表示が遅い(LCP / FCP) 最初に読み込まれる HTML が空

Slide 47

Slide 47 text

HTML DOM JS JS Pre-rendering Node.js ブラウザ レンダリング JSON API サーバ DB (CSR) コンテンツを含む コンテンツあり インタラクション LCP が速い INP が遅 い! JSON 同じコードベース

Slide 48

Slide 48 text

SPA(CSR + Pre-rendering) 最初に配信される HTML にコンテンツをPre-render Pre-rendering には CSR と同じコードベースを利用 例: React, VueJS, etc. Pre-rendering には Node.js を用いる renderToString()。DOM 操作の代わりに HTML 文字列 「初期表示が遅い」問題を解決 初期表示後は通常通り CSR と同じように動作

Slide 49

Slide 49 text

Hydration HTML 描画 JS の読み込み Hydration Hydration とは、SSRで生成された静的なHTMLに Reactが再度アタッチするプロセス

Slide 50

Slide 50 text

ページ単位で事前レンダリングの種類を選択 SSR( Server-side Rendering ) HTTP リクエストごとに JS ランタイム必須。renderToString() SG( Static Generation ) ビルド時にPre-rendering JS ランタイム不要 ISR ( Incremental Static Regeneration ) SSG + SSR( Pre-renderingとオンデマンド ) revalidate: 5[s] JS ランタイム必須 Pre-rendering の種類 MPA の クラシックSSR と 区別する場合は SSR with Hydration と呼ばれる

Slide 51

Slide 51 text

Next.js の登場 The React Framework for the Web Web のための React フレームワーク 開発元は Vercel 火曜に障害起こしがちなのなんとかしてくれ フルスタック FW 界を統一しようとしてる Rails などと同じ土俵で戦っている 当初は SSR を提供するだけのものだった 今では Zero Config や Routing など React がカバーしきれない部 分を補完

Slide 52

Slide 52 text

「TypeScript と React / Next.js でつくる実践 Web アプリケーション開発」から引用

Slide 53

Slide 53 text

✅ アジェンダ  JavaScript の発展と Ajax  SPAの流行と各 FW の乱立  React の登場と宣言的 UI  Next.js の登場と Pre-rendering  React ServerComponents と App Router 1. 2. 3. 4. 5.

Slide 54

Slide 54 text

Pre-rendering の問題点 SSR 実行時の時間とコスト サーバーからレスポンス返ってくるまでの時間が長い TTFB( Time to First Byte) が長い 特に データ取得が必要なページ・更新頻度高いページなど... SSG / ISR では根本的解決にはならない INP / TTI が遅い JS がロードされるまで「見えるのに押せない」 LCP は速いが、TTI がおっそい Hydration までの時間と Hydration 自体の時間 これの解が React Server Components 1. 2.

Slide 55

Slide 55 text

Pre-rendering の問題点 SSR 実行時の時間とコスト サーバーからレスポンス返ってくるまでの時間が長い TTFB( Time to First Byte) が長い 特に データ取得が必要なページ・更新頻度高いページなど... SSG / ISR では根本的解決にはならない INP / TTI が遅い JS がロードされるまで見えるのに押せない LCP は速いが、TTI がおっそい。 Hydration までの時間と Hydration 自体の時間 これの解が React Server Components 1. 2.

Slide 56

Slide 56 text

Service Worker Storage Local Cache CDN キャッシュ App memory cache db ブラウザ CDNエッジ アプリケーション

Slide 57

Slide 57 text

Service Worker Storage Local Cache CDN App memory cache db なるべくユーザーに近い位置でキャッシュして高速に返却することで、 パフォーマンス向上させる

Slide 58

Slide 58 text

できあがったところから返した らいいんじゃね?? Streaming SSR

Slide 59

Slide 59 text

Streaming SSR Step1: 初期 HTML を描画(non-interactive)

Slide 60

Slide 60 text

Step2: JS を実行して描画できたところから hydrate

Slide 61

Slide 61 text

Step3: サーバーで遅れて描画された部分が送られてくる (Selective Hydration)

Slide 62

Slide 62 text

Step4: 残った部分を Hydrate

Slide 63

Slide 63 text

Suspense 非同期的にレンダリングされるコンポーネントを扱うた めに必要なもの Concurrent Rendering(レンダリングの非同期化) https://vercel.com/blog/how-react-18-improves-application- performance Suspense 単位でストリーミングされたり、ハイドレ ーションされる データ取得中のローディングを宣言的に記述

Slide 64

Slide 64 text

Suspense

Slide 65

Slide 65 text

Pre-rendering の問題点 SSR 実行時の時間とコスト サーバーからレスポンス返ってくるまでの時間が長い TTFB( Time to First Byte) が長い 特に データ取得が必要なページ・更新頻度高いページなど... SSG / ISR では根本的解決にはならない INP / TTI が遅い JS がロードされるまで見えるのに押せない LCP は速いが、TTI がおっそい これの解が React Server Components 1. 2.

Slide 66

Slide 66 text

JavaScript is the web's CO2. 肥大化するクライアント JS SPA などの技術がコモディティ化 JS は Web の CO2!?!? さらに ハイドレーションの問題もある hydrateRoot(document.getElementById('root'), ); SSR は同じ React アプリケーションをサーバとクライアントの 2ヶ所でレンダリングする 無駄が多い クライアント側で実行しなきゃいけない JS の量は減っていない

Slide 67

Slide 67 text

Selective Hydration Hydrate.. 中断 Hydrate... クリック

Slide 68

Slide 68 text

特定の Hydration 処理を一時中 断して、別の箇所のハイドレー ション処理を優先的に進められ る!!

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

これだとただ Hydration のタイ ミングを変えただけで、ロード する JS の総量は変化していない (たしかに、JS のロード時間自体は短縮されているが...)

Slide 71

Slide 71 text

ロードする JS の総量そのものを 減らしせないのか....

Slide 72

Slide 72 text

Server Component + Client Component HTML + Client Component HTML + JavaScript Client (ブラウザ) React Server Components Server Stage0 Stage1 実態としては 仮想 DOM をシリ アライズしたもの

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

React Server Components 多段階計算 → バンドルサイズ↓ Reactアプリケーションの中に、サーバーで実行される部分とクライ アント側で実行される部分が混在 Stage0 = サーバー側、Stage1 = クライアント側 Stage0 のプログラム → Stage1 のプログラム → 出力結果 クラシカルSSR + jQuery との類似性 「PHP の再来」。螺旋 Stage0 =
<%= @us er.name %>
Stage 1 には HTML + JavaScript が出力される RSC が進化しているのは2つの段階が両方ともReactである点

Slide 75

Slide 75 text

Server Component + Client Com ponent HTML + Client C omponent Client (ブラウザ) React Server Components(+ SSR) Server Stage 0 HTML Rendering JavaScript Stage 1 Hydration

Slide 76

Slide 76 text

HTML M C V DOM JS テンプレート Webサーバ 依 存 技術の螺旋 APサーバ ブラウザ DB data React へ統一

Slide 77

Slide 77 text

App Router Next.js 13.4 から stable app/配下が Server Componentとして扱われる テンプレートエンジンとして考えればこの扱いも納得がいく サーバーサイド用のテンプレートを用いるときは生で使うより FW に乗せて使うことが多いよね! Client Component はオプトイン ほかにもキャッシュ周りやParallel Routes,  ServerActionsといった機能も追加されている

Slide 78

Slide 78 text

Thank you !!!

Slide 79

Slide 79 text

参考資料 React 研修 https://speakerdeck.com/recruitengineers/react-2023 Next.jsから学ぶWebレンダリング ~React誕生以前からApp Router with RSCまでの流れ~ https://zenn.dev/suzu_4/articles/2e6dbb25c12ee5 一言で理解するReact Server Components https://zenn.dev/uhyo/articles/react-server-components-multi-stage#react-server- components モダンウェブフロントエンド勉強会を開催しました https://techlife.cookpad.com/entry/2022/06/21/130736 SPA化するMPAとMPA化するSPA https://gihyo.jp/article/2022/11/tfen001-mpa_spa