Script และ Bundle Optimization
ไอเดียหลักในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียหลักในหนึ่งประโยค”ส่ง JavaScript ให้น้อยลงด้วยการโหลด third-party script ในจังหวะที่เหมาะสมผ่าน next/script, code-split client component ที่หนักด้วย next/dynamic และรักษาเส้น Server/Client boundary ให้แคบไว้
next/script และ strategy ของตัวเอง
หัวข้อที่มีชื่อว่า “next/script และ strategy ของตัวเอง”tag <script> ดิบ ๆ block การ parse และรันทันทีที่ browser ไปถึง next/script ให้คุณเลือกจังหวะด้วย strategy เลือกตัวที่ช้าที่สุดเท่าที่ script ทนได้
beforeInteractive— โหลดก่อนโค้ด Next.js ใด ๆ จะ hydrate เก็บไว้ให้ script ที่วิกฤตจริง ๆ เท่านั้นafterInteractive— ค่า default โหลดหลัง hydration เริ่ม เหมาะกับ analyticslazyOnload— โหลดตอน browser ว่าง เหมาะกับ widget ความสำคัญต่ำอย่าง chat
// A chat widget is low priority — defer it to idle timeimport Script from 'next/script'
export default function Layout({ children }: { children: React.ReactNode }) { return ( <> {children} <Script src="https://widget.example.com/chat.js" strategy="lazyOnload" /> </> )}code-split client component ด้วย next/dynamic
หัวข้อที่มีชื่อว่า “code-split client component ด้วย next/dynamic”next/dynamic (สร้างบน React.lazy) แยก client component ที่หนักออกเป็น chunk ของตัวเองที่โหลดเฉพาะตอน render สำหรับ component ที่แตะ browser-only API ให้ส่ง ssr: false เพื่อไม่ให้ component นั้นรันบน server เลย โปรดจำว่า ssr: false ใช้ได้เฉพาะภายใน Client Component
'use client'import dynamic from 'next/dynamic'
// Heavy, browser-only — split into its own chunk, no SSRconst Chart = dynamic(() => import('./Chart'), { ssr: false, loading: () => <p>Loading chart…</p>,})
export default function Dashboard() { return <Chart />}optimizePackageImports และ bundle analyzer
หัวข้อที่มีชื่อว่า “optimizePackageImports และ bundle analyzer”library บางตัวที่มี barrel file ดึงเข้ามามากกว่าที่คุณใช้จริงเยอะ optimizePackageImports บอก Next.js ให้ import เฉพาะ module ที่คุณอ้างถึงจริง ทำให้ client bundle เล็กลง
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { experimental: { optimizePackageImports: ['lucide-react'], },}
export default nextConfigจะดูว่าจริง ๆ มีอะไรอยู่ใน bundle ให้ครอบ config ด้วย @next/bundle-analyzer แล้วสั่ง build โดยเซ็ต flag ไว้
// next.config.js — run: ANALYZE=true next buildconst withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true',})
module.exports = withBundleAnalyzer({})รักษา Server/Client boundary ให้แคบ
หัวข้อที่มีชื่อว่า “รักษา Server/Client boundary ให้แคบ”ชัยชนะเรื่อง bundle ที่ใหญ่ที่สุดคือการไม่ส่งโค้ดเลย ทุกอย่างที่ import ผ่านเส้น "use client" กลายเป็น client JavaScript ดังนั้นดัน "use client" ลงไปให้ลึกที่สุดเท่าที่ทำได้ — เก็บงานที่ใช้ data หนักไว้ใน Server Component แล้ว mark เฉพาะใบ interactive เล็ก ๆ ให้เป็น client
flowchart TD Page["Server Component (0 KB client JS)"] --> Leaf["'use client' leaf"] Page --> Split["next/dynamic chunk"] Leaf --> JS[Client bundle] Split -->|loads on demand| JS Page --> Script["next/script (strategy-timed)"]