Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Next.jsから見る Webフロントエンドの歴史
Search
Yusuke Inai
October 26, 2023
Programming
1
860
Next.jsから見る Webフロントエンドの歴史
2023/10/26 RUNTEQ LT会
Yusuke Inai
October 26, 2023
Tweet
Share
More Decks by Yusuke Inai
See All by Yusuke Inai
で、エンジニアになって1年経ったけどどう?
youliangdao
1
240
人よりアウトプットができるようになるためのコツ
youliangdao
0
140
SaaSスタートアップで3ヶ月働いてみて感じた現実(リアル)
youliangdao
0
390
個人開発で挫折する人を救いたい
youliangdao
1
3k
Qiitaでバズりやすい記事の書き方を伝授する
youliangdao
0
3k
React って本当に使う意味あるの? 〜SPA と React の「キホン」の「キ」〜
youliangdao
1
200
PumaとUnicornって結局何なん!?
youliangdao
0
670
"ぼくのかんがえたさいきょうの"勉強法
youliangdao
0
310
低レイヤへの誘い
youliangdao
0
240
Other Decks in Programming
See All in Programming
なまけものオバケたち -PHP 8.4 に入った新機能の紹介-
tanakahisateru
1
120
103 Early Hints
sugi_0000
1
230
Monixと常駐プログラムの勘どころ / Scalaわいわい勉強会 #4
stoneream
0
280
バグを見つけた?それAppleに直してもらおう!
uetyo
0
180
モバイルアプリにおける自動テストの導入戦略
ostk0069
0
110
フロントエンドのディレクトリ構成どうしてる? Feature-Sliced Design 導入体験談
osakatechlab
8
4.1k
これでLambdaが不要に?!Step FunctionsのJSONata対応について
iwatatomoya
2
3.7k
HTTP compression in PHP and Symfony apps
dunglas
2
1.7k
[JAWS-UG横浜 #76] イケてるアップデートを宇宙いち早く紹介するよ!
maroon1st
0
460
Effective Signals in Angular 19+: Rules and Helpers @ngbe2024
manfredsteyer
PRO
0
140
Keeping it Ruby: Why Your Product Needs a Ruby SDK - RubyWorld 2024
envek
0
190
Symfony Mapper Component
soyuka
2
730
Featured
See All Featured
Java REST API Framework Comparison - PWX 2021
mraible
28
8.3k
Navigating Team Friction
lara
183
15k
It's Worth the Effort
3n
183
28k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
5
450
A better future with KSS
kneath
238
17k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
0
98
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
44
9.3k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
28
900
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
95
17k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
Code Reviewing Like a Champion
maltzj
520
39k
Transcript
@yusuke_blog1026 2023/10/26 Next.jsから見る Webフロントエンドの歴史
@yusuke_blog1026 Yusuke Inai Work 🏢 Counterworks Inc. 👨💻 Frontend Developer
Private 🧲 Keiba
🎯 目的 現代フロントエンドの大枠の流れの理解 Next.js や React が何を解決しようとしているのか? の理解 フロントエンドという領域そのものについての理解 (「JSON色付け係」からの脱却)
✅ アジェンダ JavaScript の発展と Ajax SPAの流行と各 FW の乱立 React の登場と宣言的
UI Next.js の登場と Pre-rendering React Server Components と App Router 1. 2. 3. 4. 5.
🙅♂️ 話さないこと React / Next.js の各機能についての説明 Hooks, Rendering, Managing State,
Suspense Routing, Cache, Server Actions 3rd Party ライブラリやツール等の使い方 UI ライブラリ, テスト, Storybook, etc. ハンズオン
✅ アジェンダ JavaScript の発展と Ajax SPAの流行と各 FW の乱立 React の登場と宣言的
UI Next.js の登場と Pre-rendering React Server Components と App Router 1. 2. 3. 4. 5.
HTML APサーバ ブラウザ いわゆるMPA(クラシックSSR) DB
※クラシックSSR SSR = サーバーサイドレンダリング リンクをクリックするたびにサーバーサイドで HTMLをレンダリングする 後述するSSRと区別 生成されるページ自体は静的
HTML M C V <html> <body> <div><%= @user.name %></div> </body>
</html> HTML = Template (data) Web MVCフレームワーク APサーバ ブラウザ DB data テンプレート
Web MVCフレームワーク サーバーサイドでは MVC を利用 Ruby on Rails, Laravel, Spring,
etc. View には「テンプレート」を利用 例: ERB, Blade, JSP, etc. HTML = Template (data) リクエスト毎にページ全体をレンダリングして返す
ブラウザ間で言語仕様が 異なり、実装者は大変 JavaScript不遇期 Web でも良質な UX が求 められ始める! インタラクティ ブなコンテンツ
の普及 JavaScriptの歴史 JavaScript台頭 Netscape Navigator と IE で 実装された ECMAScript / Flash ブラウザ間の仕様を統一する ため、ECMAScriptが策定。 Flashも登場 Ajax / HTML5 Google Map により Ajax が 注目される。またHTML5 に より API が充実 iPhone / Android ネイティブアプリの普及
JavaScriptの台頭 1995年 Netscape Navigator 2.0 に実装 翌年にはInternet Explorer 3.0 にも搭載され、急速に
普及 ECMAScriptの誕生 ブラウザ間での言語仕様を統一 開発者にとって使い勝手の良いものへ
Ajax の登場 過剰装飾で、使い勝手の悪い Web ページが量産 JS 起因のクラッシュや悪用ウイルスも流行 JS をオフにする人も... 2005
年 Ajax 登場 Google Map 革命 HTML5 により APIが強化され、機能が充実 JavaScript 復権
HTML M C V APサーバ ブラウザ DB MPA(クラシックSSR + jQuery)
インタラクション Webサーバ <script src=”...” /> DOM イベントハンドラ 登録 イベント DOMを操作 JS
MPA(クラシックSSR + jQuery) クラシック SSR が生成した HTML と連携する JS を
あとづけ コンテンツ、スタイル、ロジックを分離するのがベストプラク ティスだった 2006 年 jQuery 「write less, do more」 ブラウザ間の差異を吸収
MPA(クラシックSSR + jQuery)の課題: アプリケーションの構造
MPA(クラシックSSR + jQuery)の課題: アプリケーションの構造 命令的なイベントハンドラ DOMを参照→処理を行う→DOMを更新 「変化の過程」をひとつずつ記述 大量のイベントハンドラが散らばる DOM更新処理も散らばる DOMが巨大でグローバル変数になってしまう
破壊も容易
HTML M C V DOM JS テンプレート フロントエンド側で 開発するリソース サーバーサイド側で
開発するリソース 開発リソースではない MPA(クラシックSSR + jQuery)の課題: ワークフロー APサーバ ブラウザ DB Webサーバ 依 存 齟齬 スケジュール 二重開発
MPA(クラシックSSR + jQuery)の課題: ワークフロー 「HTML」はリソースではない リソースは「テンプレート」 JS の処理対象となるのは AP サーバの実行結果
JS 側が必要とする HTML の修正は誰がするのか? テンプレートの修正。サーバーサイド側? 開発サイクルが異なる 二重開発 サーバーサイドのテンプレートとフロントの JS が機能的に重複
✅ アジェンダ JavaScript の発展と Ajax SPAの流行と各 FW の乱立 React の登場と宣言的
UI Next.js の登場と Pre-rendering React Server Components と App Router 1. 2. 3. 4. 5.
HTML DOM JS SPA (Single Page Application) サーバ ブラウザ フォームをサブミット
イベント DOM更新 イベント DOM更新 JSON JSON リンクをクリック ブクマ等から ロードされる HTML ページは1つ (Documentオブジェクトは不変)
SPA (Single Page Application) Single Page Application ナビゲーションの画面遷移もブラウザ上の JS でレンダリング
単一の HTML ページ(あるいは Document)だけで構成 ナビゲーションの単位も「ページ」と呼ばれる 例)トップページ、一覧ページ、商品ページ、etc.
静的 HTML DOM JS SPA の起動シーケンス Web サーバ ブラウザ レンダリング
インタラクション JSON API サーバ JSON DB (CSR) イベント レンダリング (CSR) コンテンツとしては空 (SSR 不要)
SPA の起動シーケンス 基本的には CSR (Client-Side Rendering)のみ クライアントサイド(ブラウザ)だけでコンテンツをレンダリ ング サーバサイドでは HTML
ページを動的にレンダリングしない ここでの「レンダリング」とは、単純な HTML 生成処理 広義の「レンダリング」 react.dev でいうところの “Rendering” とは違う
フロントエンドの概念的な話 UI ユーザー システム
State View Action
フロントエンドと状態 クライアントサイドで状態を扱うことが増える ステートレス→ステートフル 画面の表示 → ユーザのアクション → 状態の変化 → 画面の表
示... 状態の例(保持されていて変化・変更するもの) TODOリストのデータ(Model State) モーダルの開閉状態(UI State) どのページを開いているか?(Location State) 誰がログインしているか?(Auth State) 状態は「スコープ」と「ライフタイム」を持つ
MV* フレームワークと状態 クラシックSSR + jQuery DOM 状態 イベントハンドラ 参照 更新
イベント MV* フレームワーク DOM Controller イベント View 更新 Model 状態 更新要求 変更通知
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 がフロント側で完結
MV* フレームワークの課題 Angular JS は双方向バインディングの実装アルゴリズ ムに難あり 監視する値が増えるとパフォーマンスが落ちる View と Model
の同期 手続き的な処理が残っている...
✅ アジェンダ JavaScript の発展と Ajax SPAの流行と各 FW の乱立 React の登場と宣言的
UI Next.js の登場と Pre-rendering React ServerComponents と App Router 1. 2. 3. 4. 5.
React The library for web and native user interfaces Web
とネイティブユーザインターフェースのためのライブラリ 開発元は Meta (旧 Facebook) フロントエンド界の(現)覇者 語りたいことは色々あるが、結局は以下の 2 つに集約 される コンポーネント指向 宣言的 UI ⇔ UI = f (state)
コンポーネント指向 MV* DOM Controller イベント View コンテンツ Model 状態 更新要求
変更通知 React DOM ロジック ロジック 状態 コンテンツ Component ロジック 状態 コンテンツ Component ロジック 状態 コンテンツ Component 更新 更新 子コンポーネント (複数可)
コンポーネント指向 React の構成要素はコンポーネント Model, Controllerなどは不要。 View と Modelの同期も気にする必要ない コンポーネントが持つもの ロジック、イベントハンドラ
状態 コンテンツ(JSX) コンポーネントは親子関係を持つ 各部分を担当するコンポーネントにわける メンテナンス性や再利用性↑
<form> <input type=”checkbox”> ... </form> 宣言的 UI ⇔ UI =
f (state) DOM(UI) 状態 f (レンダリング) <form> <input type=”checkbox” checked> ... </form> 状態 f (レンダリング) ユーザー操作による変化 ・・・ DOM(UI)
宣言的 UI ⇔ UI = f (state) React のメンタルモデル 「変化の過程」ではなく「結果」を記述(=宣言的)
毎回新規にレンダリングするのに近い。面倒な DOM 更新は React 側が担う 「状態」と「UI」を明確に分離 →「状態」が SSoT UI = f (state)。UI は現在の状態の映写機 クラシック SSR と類似 リクエストごとにテンプレート実行 → HTML 生成 HTML = Template (data) 面倒な DOM 更新については、jQueryのお仕事 これを自力でやろうとするからつらみ...
③状態が更新 仮想 DOM A B C <div> <span> <span> “Foo”
“Bar” <div> <span> <span> “Foo” “Bar” コンポーネント 仮想DOM 実DOM A B C <div> <span> <span> “Baz” “Bar” <div> <span> <span> “Baz” “Bar” 最初の レンダリング 再レンダリング ①実行 ②反映 ④実行 ⑥差分反映 ⑤比較
③状態が更新 仮想 DOM A B C <div> <span> <span> “Foo”
“Bar” <div> <span> <span> “Foo” “Bar” コンポーネント 仮想DOM 実DOM A B C <div> <span> <span> “Baz” “Bar” <div> <span> <span> “Baz” “Bar” 最初の レンダリング 再レンダリング ①実行 ②反映 ④実行 ⑥差分反映 ⑤比較 レンダー コミット 広義のレンダリング
仮想 DOM React のメンタルモデル 「変化の過程」ではなく「結果」を記述(=宣言的) 毎回新規にレンダリングするのに近い。面倒な DOM 更新は React 側が担う
毎回全更新だと遅い そこで登場するのが「仮想 DOM」 DOM の代わりに軽量なJSオブジェクトで仮想 DOM 構築 VDOM = f (state) 差分更新(Reconciliation) 前回レンダリング時と新しい仮想 DOM を比較 → 実DOM へ反映
※仮想 DOMの補足 HTML = <div></div> DOM API = document.createElement("div") 仮想
DOM 構造体 = React.createElement("div") 仮想 DOM アルゴリズムの実行 =ReactDOM.render(<App />, el) 前後の 2 つの木構造の比較することで、その差分を埋める操作を 自動的に導出できるはず、というのがスタート地点
✅ アジェンダ JavaScript の発展と Ajax SPAの流行と各 FW の乱立 React の登場と宣言的
UI Next.js の登場と Pre-rendering React ServerComponents と App Router 1. 2. 3. 4. 5.
静的 HTML JS エンジン SPA の課題①:クローラー クローラー Web サーバ コンテンツとしては空
コンテンツがないから インデックスできない JS に対応した クローラー インデックスが遅れて しまう!
静的 HTML DOM JS SPA の課題②:初期表示 Web サーバ ブラウザ レンダリング
JSON API サーバ DB (CSR) コンテンツとしては空 (SSR 不要) コンテンツは 空 <script src=”...” /> コンテンツは 存在 LCP が遅 い!
※ 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
None
SPA の課題 MPA (クラシック SSR)からの移行コスト SEO 対策が困難 10 年代半ばのクローラーは JS非サポート
現在は Google など JS サポートクローラーあるが... インデックス遅い OGP 対応困難・通信環境に依存 初期表示が遅い(LCP / FCP) 最初に読み込まれる HTML が空
HTML DOM JS JS Pre-rendering Node.js ブラウザ レンダリング JSON API
サーバ DB (CSR) コンテンツを含む コンテンツあり <script src=”...” /> インタラクション LCP が速い INP が遅 い! JSON 同じコードベース
SPA(CSR + Pre-rendering) 最初に配信される HTML にコンテンツをPre-render Pre-rendering には CSR と同じコードベースを利用
例: React, VueJS, etc. Pre-rendering には Node.js を用いる renderToString(<App />)。DOM 操作の代わりに HTML 文字列 「初期表示が遅い」問題を解決 初期表示後は通常通り CSR と同じように動作
Hydration HTML 描画 JS の読み込み Hydration Hydration とは、SSRで生成された静的なHTMLに Reactが再度アタッチするプロセス
ページ単位で事前レンダリングの種類を選択 SSR( Server-side Rendering ) HTTP リクエストごとに JS ランタイム必須。renderToString(<App />)
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 と呼ばれる
Next.js の登場 The React Framework for the Web Web のための
React フレームワーク 開発元は Vercel 火曜に障害起こしがちなのなんとかしてくれ フルスタック FW 界を統一しようとしてる Rails などと同じ土俵で戦っている 当初は SSR を提供するだけのものだった 今では Zero Config や Routing など React がカバーしきれない部 分を補完
「TypeScript と React / Next.js でつくる実践 Web アプリケーション開発」から引用
✅ アジェンダ JavaScript の発展と Ajax SPAの流行と各 FW の乱立 React の登場と宣言的
UI Next.js の登場と Pre-rendering React ServerComponents と App Router 1. 2. 3. 4. 5.
Pre-rendering の問題点 SSR 実行時の時間とコスト サーバーからレスポンス返ってくるまでの時間が長い TTFB( Time to First Byte)
が長い 特に データ取得が必要なページ・更新頻度高いページなど... SSG / ISR では根本的解決にはならない INP / TTI が遅い JS がロードされるまで「見えるのに押せない」 LCP は速いが、TTI がおっそい Hydration までの時間と Hydration 自体の時間 これの解が React Server Components 1. 2.
Pre-rendering の問題点 SSR 実行時の時間とコスト サーバーからレスポンス返ってくるまでの時間が長い TTFB( Time to First Byte)
が長い 特に データ取得が必要なページ・更新頻度高いページなど... SSG / ISR では根本的解決にはならない INP / TTI が遅い JS がロードされるまで見えるのに押せない LCP は速いが、TTI がおっそい。 Hydration までの時間と Hydration 自体の時間 これの解が React Server Components 1. 2.
Service Worker Storage Local Cache CDN キャッシュ App memory cache
db ブラウザ CDNエッジ アプリケーション
Service Worker Storage Local Cache CDN App memory cache db
なるべくユーザーに近い位置でキャッシュして高速に返却することで、 パフォーマンス向上させる
できあがったところから返した らいいんじゃね?? Streaming SSR
<NavBar /> <SideBar /> Streaming SSR Step1: 初期 HTML を描画(non-interactive)
Step2: JS を実行して描画できたところから hydrate <NavBar /> <SideBar />
<NavBar /> <SideBar /> <Comments /> Step3: サーバーで遅れて描画された部分が送られてくる (Selective Hydration)
Step4: 残った部分を Hydrate <NavBar /> <SideBar /> <Comments />
Suspense 非同期的にレンダリングされるコンポーネントを扱うた めに必要なもの Concurrent Rendering(レンダリングの非同期化) https://vercel.com/blog/how-react-18-improves-application- performance Suspense 単位でストリーミングされたり、ハイドレ ーションされる
データ取得中のローディングを宣言的に記述
Suspense
Pre-rendering の問題点 SSR 実行時の時間とコスト サーバーからレスポンス返ってくるまでの時間が長い TTFB( Time to First Byte)
が長い 特に データ取得が必要なページ・更新頻度高いページなど... SSG / ISR では根本的解決にはならない INP / TTI が遅い JS がロードされるまで見えるのに押せない LCP は速いが、TTI がおっそい これの解が React Server Components 1. 2.
JavaScript is the web's CO2. 肥大化するクライアント JS SPA などの技術がコモディティ化 JS
は Web の CO2!?!? さらに ハイドレーションの問題もある hydrateRoot(document.getElementById('root'), <App />); SSR は同じ React アプリケーションをサーバとクライアントの 2ヶ所でレンダリングする 無駄が多い クライアント側で実行しなきゃいけない JS の量は減っていない
Selective Hydration Hydrate.. 中断 Hydrate... クリック
特定の Hydration 処理を一時中 断して、別の箇所のハイドレー ション処理を優先的に進められ る!!
None
これだとただ Hydration のタイ ミングを変えただけで、ロード する JS の総量は変化していない (たしかに、JS のロード時間自体は短縮されているが...)
ロードする JS の総量そのものを 減らしせないのか....
Server Component + Client Component HTML + Client Component HTML
+ JavaScript Client (ブラウザ) React Server Components Server Stage0 Stage1 実態としては 仮想 DOM をシリ アライズしたもの
None
React Server Components 多段階計算 → バンドルサイズ↓ Reactアプリケーションの中に、サーバーで実行される部分とクライ アント側で実行される部分が混在 Stage0 =
サーバー側、Stage1 = クライアント側 Stage0 のプログラム → Stage1 のプログラム → 出力結果 クラシカルSSR + jQuery との類似性 「PHP の再来」。螺旋 Stage0 = <div><%= @us er.name %></div> Stage 1 には HTML + JavaScript が出力される RSC が進化しているのは2つの段階が両方ともReactである点
Server Component + Client Com ponent HTML + Client C
omponent Client (ブラウザ) React Server Components(+ SSR) Server Stage 0 HTML Rendering JavaScript Stage 1 Hydration
HTML M C V DOM JS テンプレート Webサーバ 依 存
技術の螺旋 APサーバ ブラウザ DB data React へ統一
App Router Next.js 13.4 から stable app/配下が Server Componentとして扱われる テンプレートエンジンとして考えればこの扱いも納得がいく
サーバーサイド用のテンプレートを用いるときは生で使うより FW に乗せて使うことが多いよね! Client Component はオプトイン ほかにもキャッシュ周りやParallel Routes, ServerActionsといった機能も追加されている
Thank you !!!
参考資料 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