Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code SplittingによるWebVitalsの向上

Code SplittingによるWebVitalsの向上

■イベント
プロダクトの急成長を支えるフロントエンド改善の取り組みhttps://sansan.connpass.com/event/305970/

■登壇概要
タイトル:Code SplittingによるWebVitalsの向上
登壇者:技術本部 Bill One Engineering Unit 神⽥ 伶樹

■Bill One エンジニア 採用情報
https://media.sansan-engineering.com/billone-engineer

SansanTech

January 24, 2024
Tweet

More Decks by SansanTech

Other Decks in Technology

Transcript

  1. 写真が入ります 神⽥ 伶樹 Sansan株式会社 技術本部 Bill One Engineering Unit Smart

    受領グループ 2020年 プロックチェーン系のスタートアップでエンジニア をスタート。React, React Nativeを書き始める。いくつかの Webサイト、ネイティブアプリの開発に従事。 2021年 Sansanに⼊社。バックエンドからフロントエンドま で⼀気通貫で開発中。新しい画⾯のUI作ってる時が⼀番楽し い。
  2. アジェンダ - Bill Oneアーキテクチャの概要と課題 > アーキテクチャ全体の概観 > 課題 - 前提知識

    > Code Splittingとは? > WebVitalsとは? - 実施⼿順 > 分割⽅針 > 遅延読み込み > 読み込み中のコンポーネント > ChunkLoadError対策 - 結果&考察 > ビルドファイルの⽐較 > WebVitalsの⽐較 > 考察
  3. WebVitalsとは? 9 表参道本社 神山ラボ Sansan Innovation Lab https://web.dev/articles/vitals?hl=ja ウェブ上で優れたユーザー エクスペリエンスを提供するために不可⽋な品質

    シグナルに関する統合ガイダンスの提供を⽬的とした Google の取り組み Core Web Vitals - Core Web Vitals を構成する指標は時間の経過とともに進化します。2020 年の現在のセットでは、ユーザー エクスペリエンスの 3 つの側⾯(読み 込み、インタラクティビティ、視覚的な安定性)に焦点を当てています。
  4. 分割⽅針 11 表参道本社 神山ラボ Sansan Innovation Lab - URLの1番⽬のpath名に応じて分割する。 {BASE_URL}/recipient

    → chunk {BASE_URL}/sender → chunk {BASE_URL}/issuing → chunk BillOneでは1番⽬のpathが⼀つの業務におおよそ対応しているので、異なる業務に移動する ときに、別のモジュールがダウンロードされることを期待する。 ページ毎に分割するという考え⽅もあるが、ページ遷移時のスムーズさが損なわれる。
  5. - 動的import const { RecipientRoutes } = lazy(() => import("./recipient"))

    const { SenderRoutes } = lazy(() => import("./sender")) - 通常importとの⽐較 import { RecipientRoutes } from "./recipient" import { SenderRoutes } from "./sender" Reactがコンポーネントを初めてレンダーしようとするときまで、lazy関数の引数は呼びだ されない。 遅延読み込み (1) 12 表参道本社 神山ラボ Sansan Innovation Lab https://ja.react.dev/reference/react/lazy
  6. 遅延読み込み (2) 13 表参道本社 神山ラボ Sansan Innovation Lab - lazy関数

    React標準のlazy関数はデフォルトExportしか対応していない。Bill OneではコンポーネントのExportは名前 付きExportを推奨しているため、標準の関数をそのまま適⽤できない。 export const lazy = <T extends {}, U extends keyof T>(loader: () => Promise<T>) => new Proxy({} as T, { get: (target, componentName: string | symbol) => { if (typeof componentName === "string") { return reactLazy(() => loader().then((x) => ({ default: x[componentName as U] as unknown as ComponentType<unknown>, })), ) }) https://github.com/facebook/react/issues/14603#issuecomment-736878172
  7. 読み込み中のコンポーネント 14 表参道本社 神山ラボ Sansan Innovation Lab - サスペンスを使ったコンポーネントの遅延読み込み ※

    Outletは、ネストされたルートに対するコンポーネントを使⽤するためのものです。 import { Suspense } from "react" import { Outlet } from "react-router-dom" <Suspense fallback={<AnonymousLoadingPage />}> <Outlet /> </Suspense>
  8. ChunkLoadError対策 17 表参道本社 神山ラボ Sansan Innovation Lab 原因: Chunkファイルはビルドごとに別のハッシュ値が割り当てられる。新しいリリースがあると、サーバ側に 古いハッシュのファイルを問い合わせてしまい、import

    errorになってしまう。 対策: 該当のエラーをキャッチしたらリロードするようにする。 import { useRouteError } from "react-router-dom" const error = useRouteError() if (String(error).includes("dynamically imported")) { window.location.reload() return null }
  9. ビルドファイルの⽐較 19 表参道本社 神山ラボ Sansan Innovation Lab - Code Splitting前のビルド時jsファイル群

    vendor.xxx.js 2486 KiB index.xxxx.js 5213 KiB - Code Splitting後のビルド時jsファイル群 27ファイルに分割され、0.23 〜 2266.66 Kibに分布している。
  10. WebVitalsの⽐較 20 表参道本社 神山ラボ Sansan Innovation Lab Code Splitting前のWebVitals Code

    Splitting後のWebVitals SentryでCode Splitting対応前後1週間のWebVitalsを調べた。 - First Paint (FP): 最初のpixelがレンダーされるまでの時間 - First Contentful Pain (FCP): 最初の画像、テキスト、その他DOMがレンダーされるまでの 時間 - Largest Contentful Paint (LCP): 最⼤サイズの画像、テキスト、その他DOMがレンダーさ れるまでの時間
  11. WebVitalsの⽐較 21 表参道本社 神山ラボ Sansan Innovation Lab Code Splitting前のWebVitals Code

    Splitting後のWebVitals SentryでCode Splitting対応前後1週間のWebVitalsを調べた。 - First Paint (FP): 1.36s → 988.58ms 27.4%減 🎉 - First Contentful Pain (FCP): 1.79s → 1.08s 40%減 🎉 - Largest Contentful Paint (LCP): 1.80s → 1.62s 10%減 🎉
  12. 考察 22 表参道本社 神山ラボ Sansan Innovation Lab - 最⼤Chunkファイルサイズを約半分にすることができたが、Vite推奨の500KiB以下にす ることはできなかった。翻訳ファイルのコード分割が課題。初回ロード時に巨⼤翻訳フ

    ァイルを読み込んでいる。Bundle Analyzer(*1)を使うと分析が可能。 - どのコンポーネント単位で動的importするか検討が必要。より細かく分割すれば、初回 ロードファイルサイズは⼩さくなるが、ページ遷移時のぬるぬる感がなくなってしまう。 - さらに分割して⾏っても⽬標の500KiBには届かなそうだった。Bundle Analyzerを使⽤ して個別に分析していくことが必要。 *1 https://github.com/btd/rollup-plugin-visualizer