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

Next.js Imageコンポーネントの 舞台裏をのぞく👀

Avatar for ya2s ya2s
June 25, 2024
17

Next.js Imageコンポーネントの 舞台裏をのぞく👀

Avatar for ya2s

ya2s

June 25, 2024
Tweet

Transcript

  1. CONFIDENTIAL © 2023 Reiwa Travel, Inc. 2 Background 2017.03~ LINEϠϑʔגࣜձࣾ

    ɹɹɹɹ DevOpsɺPOɺPayPayϑϦϚͷϑϩϯτΤϯυ 2022.11~ גࣜձࣾྩ࿨τϥϕϧ ɹɹɹɹ ϑϩϯτΤϯυ #Next.js #React #ReactNative #Expo #Typescript #Node.js #Neovim #Go #Python 👶 Ұࣇͷ෕ɹ🎲 ϘʔυήʔϜ SNS ྩ࿨τϥϕϧ ϑϩϯτΤϯυΤϯδχΞ দຊ ହ޾ @_yamatsum @yamatsum
  2. AGENDA © 2023 Reiwa Travel, Inc. 5 1. next/imageͷओཁػೳ •

    Reactίϯϙʔωϯτ • ը૾API • ը૾࠷దԽ 2. ͓ۚͷ࿩
  3. © 2023 Reiwa Travel, Inc. Reactίϯϙʔωϯτ Imageίϯϙʔωϯτ͸ɺHTMLͷimgλάΛϥοϓͨ͠Reactίϯϙʔωϯτ👶 Imgλάͱಉ༷ʹsrcଐੑͰը૾ύεΛࢦఆ͠ɺaltଐੑͰ୅ସςΩετΛઃఆ 8 import

    Image from "next/image"; import imgSrc from "./assets/appImage.webp"; function Page() { return ( <Image src={appImage.src} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } <img alt="NEWTΞϓϦը໘Πϝʔδ" loading="lazy" width="289" height="245" decoding="async" data-nimg="1" srcset=“/_next/image? url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f9 0f.webp&amp;w=384&amp;q=75 1x, /_next/image? url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f9 0f.webp&amp;w=640&amp;q=75 2x" src="/_next/image? url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f9 0f.webp&amp;w=640&amp;q=75" ></img>;
  4. © 2023 Reiwa Travel, Inc. Reactίϯϙʔωϯτ - srcsetଐੑ srcsetଐੑʹෳ਺ͷը૾ͷURLͱͦΕͧΕͷ෯Λࣗಈతʹઃఆ 9

    <img alt="NEWTΞϓϦը໘Πϝʔδ" loading="lazy" width="289" height="245" decoding="async" data-nimg="1" srcset=“/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=384&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=640&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=640&amp;q=75" ></img>;
  5. © 2023 Reiwa Travel, Inc. Reactίϯϙʔωϯτ - loadingଐੑ ஗ԆಡΈࠐΈͰॳظදࣔΛߴ଎Խ🚄 σϑΥϧτͰlazyʹઃఆ

    10 <img alt="NEWTΞϓϦը໘Πϝʔδ" loading="lazy" width="289" height="245" decoding="async" data-nimg="1" srcset=“/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=384&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=640&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=640&amp;q=75" ></img>;
  6. © 2023 Reiwa Travel, Inc. Reactίϯϙʔωϯτ - ϩʔΧϧը૾ͷѻ͍ 1. ը૾Λ૬ରύεͰimportͯ͠src

    propsʹ౉͢ 2. publicσΟϨΫτϦ͔ΒͷઈରύεΛsrc propsʹจࣈྻͰ౉͢ 11 import Image from "next/image"; import imgSrc from "./assets/appImage.webp"; function Page() { return ( <Image src={appImage.src} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } import Image from "next/image"; function Page() { return ( <Image src={"/assets/appImage.webp"} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } 1 2
  7. © 2023 Reiwa Travel, Inc. Reactίϯϙʔωϯτ - ϩʔΧϧը૾ͷѻ͍ 12 import

    Image from "next/image"; import imgSrc from "./assets/appImage.webp"; function Page() { return ( <Image src={appImage.src} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } import Image from "next/image"; function Page() { return ( <Image src={"/assets/appImage.webp"} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } 1 2 Cache-Control: public,max-age=31536000,immutable Cache-Control: public, max-age=0 🤔 😚
  8. © 2023 Reiwa Travel, Inc. Reactίϯϙʔωϯτ - ϩʔΧϧը૾ͷѻ͍ 13 import

    Image from "next/image"; import imgSrc from "./assets/appImage.webp"; function Page() { return ( <Image src={appImage.src} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } import Image from "next/image"; function Page() { return ( <Image src={"/assets/appImage.webp"} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } 1 2 /_next/image?url=/_next/static /_next/image?url=/assets
  9. © 2023 Reiwa Travel, Inc. ը૾API ੜ੒͞Εͨimgλάͷsrcsetଐੑͷύε͕/_next/image͔Β࢝·͍ͬͯΔ ը૾࠷దԽAPIͷΤϯυϙΠϯτ ⚠ basePathΛར༻͍ͯ͠Δ৔߹͸ҟͳΔ

    15 <img alt="NEWTΞϓϦը໘Πϝʔδ" loading="lazy" width="289" height="245" decoding="async" data-nimg="1" srcset=“/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=384&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=640&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f90f.webp&amp;w=640&amp;q=75" ></img>;
  10. © 2023 Reiwa Travel, Inc. ը૾API - ը૾࠷దԽAPIͷ໾ׂ ը૾ͷϦαΠζ ΫϥΠΞϯτͷσόΠεͷը໘αΠζ΍ղ૾౓ɺImageίϯϙʔωϯτͷwidthɺheightɺlayoutϓϩύςΟʹԠͯ͡ɺ࠷దͳαΠζͷ

    ը૾Λੜ੒ɻ ը૾ͷϑΥʔϚοτม׵ ΫϥΠΞϯτͷϒϥ΢β͕αϙʔτ͢Δ࠷దͳը૾ϑΥʔϚοτʢWebPɺAVIFͳͲʣʹม׵͢Δɻ͜ΕʹΑΓɺϑΝΠϧαΠζΛେ ෯ʹ࡟ݮ͠ɺಡΈࠐΈ଎౓Λ޲্ͤ͞Δ͜ͱ͕Ͱ͖Δɻ ը࣭ͷ࠷దԽ ࢦఆ͞Εͨը࣭ʢqualityϓϩύςΟʣʹԠͯ͡ɺը࣭Λௐ੔͢Δɻߴը࣭ͳը૾͕ඞཁͳ৔߹͸ߴը࣭Ͱɺͦ͏Ͱͳ͍৔߹͸௿ը࣭Ͱ ഑৴͢Δ͜ͱͰɺσʔλ௨৴ྔΛઅ໿Ͱ͖Δɻ Ωϟογϡ ੜ੒ͨ͠ը૾ΛΩϟογϡ͢Δ͜ͱͰɺ2ճ໨Ҏ߱ͷϦΫΤετʹ͸Ωϟογϡ͞Εͨը૾Λଈ࠲ʹฦ͢͜ͱ͕Ͱ͖Δɻ͜ΕʹΑΓɺ αʔόʔͷෛՙΛܰݮ͠ɺϖʔδͷදࣔ଎౓Λ޲্ͤ͞Δ͜ͱ͕Ͱ͖Δɻ 16
  11. © 2023 Reiwa Travel, Inc. ը૾API - ը૾࠷దԽAPI΁ͷϦΫΤετύϥϝʔλ /_next/image΁ͷϦΫΤετ url:

    ࠷దԽ͍ͨ͠ը૾ͷURL w: ϦΫΤετ͢Δը૾ͷ෯ʢϐΫηϧ୯Ґʣ q: ϦΫΤετ͢Δը૾ͷ඼࣭ʢ0ʙ100ʣ url: /_next/static/media/app_image_mobile.webp w: 640 q: 75 17 /_next/image?url=/_next/static/media/app_image_mobile.webp&w=640&q=75 next/imageΛར༻͠ͳͯ͘΋ɺը૾࠷దԽAPI͚ͩͰ΋ར༻Մೳ
  12. © 2023 Reiwa Travel, Inc. ը૾API - දࣔ͞ΕΔը૾ͷ෯ ≠ ಡΈࠐ·ΕΔը૾ͷ෯

    ը૾APIʹ౉͞ΕΔwͷύϥϝʔλΛݟΔͱɺImageίϯϙʔωϯτͰࢦఆͨ͠ 
 width=“289"ͱҟͳΔ஋ʹม׵͞Ε͍ͯΔ͜ͱ͕Θ͔Δ🤥 18 import Image from "next/image"; import imgSrc from "./assets/appImage.webp"; function Page() { return ( <Image src={appImage.src} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); } <img alt="NEWTΞϓϦը໘Πϝʔδ" loading="lazy" width="289" height="245" decoding="async" data-nimg="1" srcset=“/_next/image? url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f9 0f.webp&amp;w=384&amp;q=75 1x, /_next/image? url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f9 0f.webp&amp;w=640&amp;q=75 2x" src="/_next/image? url=%2F_next%2Fstatic%2Fmedia%2Fapp_image_mobile.1f97f9 0f.webp&amp;w=640&amp;q=75" ></img>;
  13. © 2023 Reiwa Travel, Inc. ը૾API - දࣔ͞ΕΔը૾ͷ෯ ≠ ಡΈࠐ·ΕΔը૾ͷ෯

    ࠷దԽʹ༻͍Δwύϥϝʔλͷ஋͸ɺdeviceSizesͱimageSizesͷ഑ྻʹΑܾͬͯఆ͞ΕΔ 19 module.exports = { images: { deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, } import Image from "next/image"; import imgSrc from "./assets/appImage.webp"; function Page() { return ( <Image src={appImage.src} alt={"NEWTΞϓϦը໘Πϝʔδ"} width={289} height={245} /> ); }
  14. © 2023 Reiwa Travel, Inc. ը૾࠷దԽ Next.js͸ը૾ͷ࠷దԽϥΠϒϥϦʹSharpΛར༻͍ͯ͠Δ Sharp͸libvipsͱ͍͏ߴੑೳͳը૾ॲཧϥΠϒϥϦΛϕʔεʹ͓ͯ͠Γɺඇৗʹߴ଎ͳॲཧ͕Մ ೳ 21

    σϑΥϧτͰ͸Squoosh͕ར༻͞ΕΔ Sharp͕Πϯετʔϧ͞Ε͍ͯΔ؀ڥͷΈSharp͕ར༻͞ΕΔ = ϩʔΧϧͱPreview/ProductionͰը૾ͷݟ͑ํʹ͕ࠩ͋Γ͏Δ ࣍ͷόʔδϣϯ͋ͨΓͰSquooshͷར༻͕׬શͷऔΓআ͔ΕΔ Sharp͕optinal dependenciesͱͯ͠Πϯετʔϧ͞ΕΔ Yarn v1͕࣮࣭αϙʔτର৅֎ͱͳΔ ⚠
  15. © 2023 Reiwa Travel, Inc. ͓ۚͷ࿩ - loader ྫ͑͹Imgix loaderͷ৔߹…

    24 export default function imgixLoader({ src, width, quality }) { const url = new URL(`https://example.com${src}`) const params = url.searchParams params.set('auto', params.getAll('auto').join(',') || 'format') params.set('fit', params.get('fit') || 'max') params.set('w', params.get('w') || width.toString()) params.set('q', (quality || 50).toString()) return url.href } <Image loader={imgixLoader} . />
  16. © 2023 Reiwa Travel, Inc. ͓ۚͷ࿩ - loader Custom loaderΛར༻ͨ͠৔߹ɺ


    ՝ۚର৅ͱͳΔnext/imageͷར༻ຕ਺ͱͯ͠Χ΢ϯτ͞ΕΔͷʁ Image Optimization pricing is dependent on your plan and how many unique source images you have across your projects during your billing period. ... A source image is the value that is passed to the src prop. https://vercel.com/docs/image-optimization/limits-and-pricing 25
  17. © 2023 Reiwa Travel, Inc. ͓ۚͷ࿩ - loader Custom loaderΛར༻ͨ͠৔߹ɺ


    ՝ۚର৅ͱͳΔnext/imageͷར༻ຕ਺ͱͯ͠Χ΢ϯτ͞ΕΔͷʁ Image Optimization pricing is dependent on your plan and how many unique source images you have across your projects during your billing period. ... A source image is the value that is passed to the src prop. https://vercel.com/docs/image-optimization/limits-and-pricing →ͱهࡌ͕͋Δ΋ͷͷɺΧ΢ϯτ͞Εͯͳͦ͞͏ʂ🤗 26