马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
在今世 Web 应用中,性能优化直接影响用户体验和业务转化。Next.js 14 提供了多种内置的性能优化特性,本日我们就来深入探究怎样充实利用这些特性,以及一些实用的优化本领。
图片和字体优化
1. 图片优化
Next.js 的 Image 组件供了强盛的图片优化功能:- // components/OptimizedImage.tsx
- import Image from 'next/image';
- import { useState } from 'react';
- interface OptimizedImageProps {
- src: string;
- alt: string;
- width: number;
- height: number;
- }
- export function OptimizedImage({
- src,
- alt,
- width,
- height
- }: OptimizedImageProps) {
- const [isLoading, setLoading] = useState(true);
- return (
- <div className="relative overflow-hidden">
- <Image
- src={src}
- alt={alt}
- width={width}
- height={height}
- quality={75} // 默认图片质量
- placeholder="blur" // 使用模糊占位
- blurDataURL="data:image/jpeg;base64,..." // 生成的 base64 图片
- className={`
- duration-700 ease-in-out
- ${isLoading ? 'scale-110 blur-2xl' : 'scale-100 blur-0'}
- `}
- onLoadingComplete={() => setLoading(false)}
- priority={false} // 是否优先加载
- />
- </div>
- );
- }
- // 使用自定义图片加载器
- const imageLoader = ({ src, width, quality }) => {
- return `https://your-cdn.com/${src}?w=${width}&q=${quality || 75}`;
- };
- // next.config.js
- /** @type {import('next').NextConfig} */
- const nextConfig = {
- images: {
- loader: 'custom',
- loaderFile: './lib/imageLoader.ts',
- deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
- imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
- formats: ['image/webp'],
- },
- };
复制代码 2. 字体优化
Next.js 14 提供了内置的字体优化:- // app/fonts.ts
- import { Inter, Roboto_Mono } from 'next/font/google';
- export const inter = Inter({
- subsets: ['latin'],
- display: 'swap',
- preload: true,
- fallback: ['system-ui', 'arial'],
- adjustFontFallback: true, // 自动调整回退字体
- });
- export const roboto_mono = Roboto_Mono({
- subsets: ['latin'],
- display: 'swap',
- });
- // app/layout.tsx
- import { inter } from './fonts';
- export default function RootLayout({
- children,
- }: {
- children: React.ReactNode;
- }) {
- return (
- <html lang="en" className={inter.className}>
- <body>{children}</body>
- </html>
- );
- }
- // 自定义字体加载
- import localFont from 'next/font/local';
- const myFont = localFont({
- src: [
- {
- path: '../public/fonts/font-regular.woff2',
- weight: '400',
- style: 'normal',
- },
- {
- path: '../public/fonts/font-bold.woff2',
- weight: '700',
- style: 'normal',
- },
- ],
- display: 'swap',
- preload: true,
- });
复制代码 动态导入和代码分割
1. 组件动态导入
- // components/DynamicComponent.tsx
- import dynamic from 'next/dynamic';
- import { Suspense } from 'react';
- // 基础动态导入
- const DynamicHeader = dynamic(() => import('./Header'), {
- loading: () => <p>Loading...</p>,
- ssr: true, // 是否服务端渲染
- });
- // 带有自定义加载的动态导入
- const DynamicChart = dynamic(
- () => import('./Chart').then(mod => mod.Chart),
- {
- loading: () => <ChartSkeleton />,
- ssr: false, // 禁用服务端渲染
- }
- );
- // 使用 Suspense 包裹动态组件
- export function DashboardPage() {
- return (
- <div>
- <DynamicHeader />
- <Suspense fallback={<ChartSkeleton />}>
- <DynamicChart />
- </Suspense>
- </div>
- );
- }
复制代码 2. 路由分组和懒加载
- // app/(marketing)/layout.tsx
- import { Suspense } from 'react';
- // 营销相关页面的布局
- export default function MarketingLayout({
- children
- }: {
- children: React.ReactNode;
- }) {
- return (
- <div className="marketing-layout">
- <Suspense fallback={<NavSkeleton />}>
- <Navigation />
- </Suspense>
- {children}
- </div>
- );
- }
- // app/(dashboard)/layout.tsx
- // 仪表板相关页面的布局
- export default function DashboardLayout({
- children
- }: {
- children: React.ReactNode;
- }) {
- return (
- <div className="dashboard-layout">
- <Suspense fallback={<SidebarSkeleton />}>
- <Sidebar />
- </Suspense>
- <main>{children}</main>
- </div>
- );
- }
复制代码 缓存战略优化
1. 数据缓存
- // lib/cache.ts
- import { cache } from 'react';
- import { Redis } from '@upstash/redis';
- const redis = new Redis({
- url: process.env.REDIS_URL,
- token: process.env.REDIS_TOKEN,
- });
- // 使用 React 缓存
- export const getCachedData = cache(async (key: string) => {
- // 首先尝试从 Redis 获取
- const cached = await redis.get(key);
- if (cached) return JSON.parse(cached);
- // 如果没有缓存,则获取新数据
- const data = await fetchData(key);
- // 存入 Redis
- await redis.set(key, JSON.stringify(data), {
- ex: 3600 // 1小时过期
- });
- return data;
- });
- // 使用示例
- async function ProductPage({ id }: { id: string }) {
- const product = await getCachedData(`product:${id}`);
- return <ProductDetails product={product} />;
- }
复制代码 2. 静态天生优化
- // app/products/[id]/page.tsx
- import { generateMetadata } from 'next';
- // 生成静态路由
- export async function generateStaticParams() {
- const products = await getTopProducts();
- return products.map((product) => ({
- id: product.id,
- }));
- }
- // 静态元数据
- export async function generateMetadata({ params }: {
- params: { id: string }
- }): Promise<Metadata> {
- const product = await getProduct(params.id);
- return {
- title: product.name,
- description: product.description,
- openGraph: {
- images: [product.image],
- },
- };
- }
- // 页面组件
- export default async function ProductPage({
- params
- }: {
- params: { id: string }
- }) {
- const product = await getProduct(params.id);
- return (
- <div>
- <h1>{product.name}</h1>
- <ProductDetails product={product} />
- </div>
- );
- }
复制代码 首屏加载优化
1. 流式渲染
- // app/page.tsx
- import { Suspense } from 'react';
- import { headers } from 'next/headers';
- async function SlowComponent() {
- const headersList = headers();
- const userAgent = headersList.get('user-agent');
- // 模拟慢速数据加载
- await new Promise(resolve => setTimeout(resolve, 2000));
- return (
- <div>
- <p>User Agent: {userAgent}</p>
- </div>
- );
- }
- export default function HomePage() {
- return (
- <div>
- <h1>即时加载的内容</h1>
- <Suspense fallback={<LoadingSkeleton />}>
- <SlowComponent />
- </Suspense>
- <Suspense fallback={<CardsSkeleton />}>
- <PopularProducts />
- </Suspense>
- <Suspense fallback={<FeedSkeleton />}>
- <RecentActivity />
- </Suspense>
- </div>
- );
- }
复制代码 2. 预加载数据
- // lib/prefetch.ts
- export async function prefetchData() {
- // 预加载关键数据
- const promises = [
- prefetchNavigation(),
- prefetchUserData(),
- prefetchPopularProducts(),
- ];
- await Promise.all(promises);
- }
- // app/layout.tsx
- export default async function RootLayout({
- children
- }: {
- children: React.ReactNode
- }) {
- // 在布局组件中预加载数据
- await prefetchData();
- return (
- <html>
- <body>{children}</body>
- </html>
- );
- }
复制代码 Core Web Vitals 优化
1. 性能监控
- // lib/analytics.ts
- export function reportWebVitals({
- id,
- name,
- label,
- value,
- }: {
- id: string;
- name: string;
- label: string;
- value: number;
- }) {
- // 发送性能指标到分析服务
- fetch('/api/analytics', {
- method: 'POST',
- body: JSON.stringify({
- id,
- name,
- label,
- value,
- // 添加其他上下文信息
- page: window.location.pathname,
- timestamp: Date.now(),
- }),
- });
- }
- // next.config.js
- /** @type {import('next').NextConfig} */
- const nextConfig = {
- experimental: {
- instrumentationHook: true,
- },
- };
- // instrumentation.ts
- export function register() {
- if (process.env.NEXT_RUNTIME === 'nodejs') {
- // 服务端监控
 - require('./monitoring/server').setup();
- }
- }
复制代码 2. 性能优化实践
- // components/OptimizedList.tsx
- import { useVirtualizer } from '@tanstack/react-virtual';
- import { useIntersectionObserver } from '@/hooks/useIntersectionObserver';
- // 虚拟列表优化
- export function OptimizedList({ items }: { items: any[] }) {
- const parentRef = useRef<HTMLDivElement>(null);
- const virtualizer = useVirtualizer({
- count: items.length,
- getScrollElement: () => parentRef.current,
- estimateSize: () => 50,
- });
- return (
- <div ref={parentRef} className="h-[500px] overflow-auto">
- <div
- style={{
- height: `${virtualizer.getTotalSize()}px`,
- position: 'relative',
- }}
- >
- {virtualizer.getVirtualItems().map((virtualItem) => (
- <div
- key={virtualItem.key}
- style={{
- position: 'absolute',
- top: 0,
- left: 0,
- width: '100%',
- height: `${virtualItem.size}px`,
- transform: `translateY(${virtualItem.start}px)`,
- }}
- >
- <ListItem item={items[virtualItem.index]} />
- </div>
- ))}
- </div>
- </div>
- );
- }
- // 图片懒加载优化
- export function LazyImage({ src, alt }: { src: string; alt: string }) {
- const imgRef = useRef<HTMLImageElement>(null);
- const { isIntersecting } = useIntersectionObserver(imgRef);
- return (
- <img
- ref={imgRef}
- src={isIntersecting ? src : ''}
- alt={alt}
- loading="lazy"
- decoding="async"
- />
- );
- }
复制代码 3. 构建优化
- // next.config.js
- const withBundleAnalyzer = require('@next/bundle-analyzer')({
- enabled: process.env.ANALYZE === 'true',
- });
- /** @type {import('next').NextConfig} */
- const nextConfig = {
- // 优化构建配置
- poweredByHeader: false,
- compress: true,
- productionBrowserSourceMaps: false,
- // 优化图片配置
- images: {
- minimumCacheTTL: 60,
- deviceSizes: [640, 750, 828, 1080, 1200, 1920],
- imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
- },
- // 实验性功能
- experimental: {
- optimizeCss: true, // 启用 CSS 优化
- scrollRestoration: true, // 启用滚动位置恢复
- serverActions: true, // 启用服务端操作
- },
- // webpack 配置
- webpack: (config, { dev, isServer }) => {
- // 优化生产环境构建
- if (!dev && !isServer) {
- config.optimization.splitChunks = {
- chunks: 'all',
- minSize: 20000,
- maxSize: 244000,
- minChunks: 1,
- maxAsyncRequests: 30,
- maxInitialRequests: 30,
- cacheGroups: {
- defaultVendors: {
- test: /[\\/]node_modules[\\/]/,
- priority: -10,
- reuseExistingChunk: true,
- },
- default: {
- minChunks: 2,
- priority: -20,
- reuseExistingChunk: true,
- },
- },
- };
- }
- return config;
- },
- };
- module.exports = withBundleAnalyzer(nextConfig);
复制代码 写在末了
Next.js 14 提供了丰富的性能优化工具和特性。在现实应用中,须要注意以下几点:
- 公道利用图片和字体优化
- 实行有用的代码分割战略
- 优化数据缓存和预加载
- 监控
和优化 Core Web Vitals
- 连续举行构建优化
在下一篇文章中,我们将深入探究 Next.js 14 的摆设与运维战略。假如你有任何题目或发起,欢迎在批评区讨论!
假如以为这篇文章对你有资助,别忘了点个赞 👍
|