nextjs-performance by giuseppe-trisciuoglio/developer-kit
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill nextjs-performance专注于核心网页指标、现代模式和最佳实践的 Next.js 应用优化专家指导。
此技能为优化 Next.js 应用程序提供全面的指导。它涵盖了核心网页指标优化(LCP、INP、CLS)、现代 React 模式、服务器组件、缓存策略和打包优化技术。专为已经熟悉 React/Next.js 并希望实施生产级优化的开发人员设计。
在开发 Next.js 应用程序并需要以下操作时使用此技能:
next/image 实现图像优化以加快加载速度next/font 配置字体优化以消除布局偏移unstable_cache、revalidateTag 或 ISR 设置缓存策略next/image 进行广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
next/font 进行字体优化unstable_cache、revalidateTag、ISR)根据您正在优化的领域加载相关参考文件:
references/image-optimization.mdreferences/font-optimization.mdreferences/caching-strategies.mdreferences/server-components.md遵循快速模式进行常见优化
应用前后转换以改进现有代码
更改后使用 Lighthouse 验证改进效果
之前(使用 useEffect 的客户端组件):
'use client'
import { useEffect, useState } from 'react'
export default function ProductList() {
const [products, setProducts] = useState([])
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts)
}, [])
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
之后(具有直接数据访问的服务器组件):
import { db } from '@/lib/db'
export default async function ProductList() {
const products = await db.product.findMany()
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
import Image from 'next/image'
export function Hero() {
return (
<div className="relative w-full h-[600px]">
<Image
src="/hero.jpg"
alt="Hero"
fill
priority // 为 LCP 禁用懒加载
sizes="100vw"
className="object-cover"
/>
</div>
)
}
import { unstable_cache, revalidateTag } from 'next/cache'
// 缓存数据函数
const getProducts = unstable_cache(
async () => db.product.findMany(),
['products'],
{ revalidate: 3600, tags: ['products'] }
)
// 在数据变更时重新验证
export async function createProduct(data: FormData) {
'use server'
await db.product.create({ data })
revalidateTag('products')
}
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
})
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.variable}>
<body className={`${inter.className} antialiased`}>
{children}
</body>
</html>
)
}
import { Suspense } from 'react'
export default function Page() {
return (
<>
<header>静态内容(立即显示)</header>
<Suspense fallback={<ProductSkeleton />}>
<ProductList /> {/* 准备就绪后流式传输 */}
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews /> {/* 独立流式传输 */}
</Suspense>
</>
)
}
处理特定领域时加载这些参考:
| 主题 | 参考文件 |
|---|---|
| 核心网页指标 | references/core-web-vitals.md |
| 图像优化 | references/image-optimization.md |
| 字体优化 | references/font-optimization.md |
| 缓存策略 | references/caching-strategies.md |
| 服务器组件 | references/server-components.md |
| 流式传输/Suspense | references/streaming-suspense.md |
| 打包优化 | references/bundle-optimization.md |
| 元数据/搜索引擎优化 | references/metadata-seo.md |
| API 路由 | references/api-routes.md |
| Next.js 16 模式 | references/nextjs-16-patterns.md |
| 从 | 到 | 优势 |
|---|---|---|
useEffect + fetch | 在服务器组件中直接使用 async | -70% JS,更快的 TTFB |
用于数据的 useState | 具有直接数据库访问的服务器组件 | 代码更简单,无需水合 |
| 客户端 fetch | unstable_cache 或 ISR | 重复加载更快 |
img 标签 | next/image | 优化格式,懒加载 |
| CSS 字体导入 | next/font | 零 CLS,自动优化 |
| 静态导入重型组件 | dynamic() | 减少初始打包大小 |
next/imageprioritywidth 和 height 或使用 fill 并指定 sizesplaceholder="blur" 以获得更好的用户体验next/font 而非 CSS 导入subsets 以减少大小display: 'swap' 以实现立即文本渲染variable 选项创建 CSS 变量unstable_cache 缓存昂贵的查询dynamic() 懒加载重型组件@next/bundle-analyzer 分析打包priority 应仅用于首屏图像fill,否则必须提供 width 和 height// Next.js 15+ 中 params 是一个 Promise
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetchPost(slug)
return <article>{post.content}</article>
}
'use client'
import { use, Suspense } from 'react'
function Comments({ promise }: { promise: Promise<Comment[]> }) {
const comments = use(promise) // 在解析前暂停
return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>
}
'use client'
import { useOptimistic } from 'react'
export function TodoList({ todos }: { todos: Todo[] }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: Todo) => [...state, newTodo]
)
async function addTodo(formData: FormData) {
const text = formData.get('text') as string
addOptimisticTodo({ id: crypto.randomUUID(), text, completed: false })
await createTodo(text)
}
return (
<form action={addTodo}>
<input name="text" />
{optimisticTodos.map(todo => <div key={todo.id}>{todo.text}</div>)}
</form>
)
}
# 安装分析器
npm install --save-dev @next/bundle-analyzer
# 运行分析
ANALYZE=true npm run build
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({
modularizeImports: {
'lodash': { transform: 'lodash/{{member}}' },
},
})
next/imagepriority 属性next/font// ❌ 不要:在 useEffect 中获取数据
'use client'
useEffect(() => { fetch('/api/data').then(...) }, [])
// ✅ 应该:在服务器组件中直接获取数据
const data = await fetch('/api/data')
// ❌ 不要:忘记为图像提供尺寸
<Image src="/photo.jpg" />
// ✅ 应该:始终提供尺寸
<Image src="/photo.jpg" width={800} height={600} />
// ❌ 不要:在所有图像上使用 priority
<Image src="/photo1.jpg" priority />
<Image src="/photo2.jpg" priority />
// ✅ 应该:仅对 LCP 图像使用 priority
<Image src="/hero.jpg" priority />
<Image src="/photo.jpg" loading="lazy" />
// ❌ 不要:对所有数据使用相同的 TTL 缓存
{ revalidate: 3600 }
// ✅ 应该:使 TTL 与数据变更频率匹配
{ revalidate: 86400 } // 分类很少变更
{ revalidate: 60 } // 评论经常变更
每周安装次数
419
代码仓库
GitHub 星标数
173
首次出现
2026年2月20日
安全审计
安装于
codex393
gemini-cli390
github-copilot388
opencode386
cursor386
kimi-cli382
Expert guidance for optimizing Next.js applications with focus on Core Web Vitals, modern patterns, and best practices.
This skill provides comprehensive guidance for optimizing Next.js applications. It covers Core Web Vitals optimization (LCP, INP, CLS), modern React patterns, Server Components, caching strategies, and bundle optimization techniques. Designed for developers already familiar with React/Next.js who want to implement production-grade optimizations.
Use this skill when working on Next.js applications and need to:
next/image for faster loadingnext/font to eliminate layout shiftunstable_cache, revalidateTag, or ISRnext/imagenext/fontunstable_cache, revalidateTag, ISR)Load relevant reference files based on the area you're optimizing:
references/image-optimization.mdreferences/font-optimization.mdreferences/caching-strategies.mdreferences/server-components.mdFollow the quick patterns for common optimizations
Apply before/after conversions to improve existing code
Verify improvements with Lighthouse after changes
BEFORE (Client Component with useEffect):
'use client'
import { useEffect, useState } from 'react'
export default function ProductList() {
const [products, setProducts] = useState([])
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts)
}, [])
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
AFTER (Server Component with direct data access):
import { db } from '@/lib/db'
export default async function ProductList() {
const products = await db.product.findMany()
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
import Image from 'next/image'
export function Hero() {
return (
<div className="relative w-full h-[600px]">
<Image
src="/hero.jpg"
alt="Hero"
fill
priority // Disable lazy loading for LCP
sizes="100vw"
className="object-cover"
/>
</div>
)
}
import { unstable_cache, revalidateTag } from 'next/cache'
// Cached data function
const getProducts = unstable_cache(
async () => db.product.findMany(),
['products'],
{ revalidate: 3600, tags: ['products'] }
)
// Revalidate on mutation
export async function createProduct(data: FormData) {
'use server'
await db.product.create({ data })
revalidateTag('products')
}
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
})
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.variable}>
<body className={`${inter.className} antialiased`}>
{children}
</body>
</html>
)
}
import { Suspense } from 'react'
export default function Page() {
return (
<>
<header>Static content (immediate)</header>
<Suspense fallback={<ProductSkeleton />}>
<ProductList /> {/* Streamed when ready */}
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews /> {/* Independent streaming */}
</Suspense>
</>
)
}
Load these references when working on specific areas:
| Topic | Reference File |
|---|---|
| Core Web Vitals | references/core-web-vitals.md |
| Image Optimization | references/image-optimization.md |
| Font Optimization | references/font-optimization.md |
| Caching Strategies | references/caching-strategies.md |
| Server Components | references/server-components.md |
| Streaming/Suspense | references/streaming-suspense.md |
| From | To | Benefit |
|---|---|---|
useEffect + fetch | Direct async in Server Component | -70% JS, faster TTFB |
useState for data | Server Component with direct DB access | Simpler code, no hydration |
| Client-side fetch | unstable_cache or ISR | Faster repeated loads |
img tag | next/image | Optimized formats, lazy loading |
| CSS font import |
next/image for all imagespriority to LCP images onlywidth and height or fill with sizesplaceholder="blur" for better UXnext/font instead of CSS importssubsets to reduce sizedisplay: 'swap' for immediate text rendervariable optionunstable_cachedynamic()@next/bundle-analyzerpriority should only be used for above-the-fold imageswidth and height are required unless using fill// Next.js 15+ params is a Promise
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetchPost(slug)
return <article>{post.content}</article>
}
'use client'
import { use, Suspense } from 'react'
function Comments({ promise }: { promise: Promise<Comment[]> }) {
const comments = use(promise) // Suspend until resolved
return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>
}
'use client'
import { useOptimistic } from 'react'
export function TodoList({ todos }: { todos: Todo[] }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: Todo) => [...state, newTodo]
)
async function addTodo(formData: FormData) {
const text = formData.get('text') as string
addOptimisticTodo({ id: crypto.randomUUID(), text, completed: false })
await createTodo(text)
}
return (
<form action={addTodo}>
<input name="text" />
{optimisticTodos.map(todo => <div key={todo.id}>{todo.text}</div>)}
</form>
)
}
# Install analyzer
npm install --save-dev @next/bundle-analyzer
# Run analysis
ANALYZE=true npm run build
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({
modularizeImports: {
'lodash': { transform: 'lodash/{{member}}' },
},
})
next/image with proper dimensionspriority attributenext/font with subsets// ❌ DON'T: Fetch in useEffect
'use client'
useEffect(() => { fetch('/api/data').then(...) }, [])
// ✅ DO: Fetch directly in Server Component
const data = await fetch('/api/data')
// ❌ DON'T: Forget dimensions on images
<Image src="/photo.jpg" />
// ✅ DO: Always provide dimensions
<Image src="/photo.jpg" width={800} height={600} />
// ❌ DON'T: Use priority on all images
<Image src="/photo1.jpg" priority />
<Image src="/photo2.jpg" priority />
// ✅ DO: Priority only for LCP
<Image src="/hero.jpg" priority />
<Image src="/photo.jpg" loading="lazy" />
// ❌ DON'T: Cache everything with same TTL
{ revalidate: 3600 }
// ✅ DO: Match TTL to data change frequency
{ revalidate: 86400 } // Categories rarely change
{ revalidate: 60 } // Comments change often
Weekly Installs
419
Repository
GitHub Stars
173
First Seen
Feb 20, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex393
gemini-cli390
github-copilot388
opencode386
cursor386
kimi-cli382
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
| Bundle Optimization | references/bundle-optimization.md |
| Metadata/SEO | references/metadata-seo.md |
| API Routes | references/api-routes.md |
| Next.js 16 Patterns | references/nextjs-16-patterns.md |
next/font| Zero CLS, automatic optimization |
| Static import of heavy component | dynamic() | Reduced initial bundle |