perf: add web vitals monitoring and optimize performance
This commit is contained in:
@@ -7,6 +7,13 @@ const nextConfig: NextConfig = {
|
|||||||
unoptimized: true,
|
unoptimized: true,
|
||||||
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
||||||
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
||||||
|
formats: ['image/webp'],
|
||||||
|
},
|
||||||
|
compress: true,
|
||||||
|
poweredByHeader: false,
|
||||||
|
reactStrictMode: true,
|
||||||
|
experimental: {
|
||||||
|
optimizePackageImports: ['lucide-react', 'framer-motion'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+19
-2
@@ -1,23 +1,29 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
import { Geist, Geist_Mono, Noto_Sans_SC } from "next/font/google";
|
import { Geist, Geist_Mono, Noto_Sans_SC } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { ThemeProvider } from "@/contexts/theme-context";
|
import { ThemeProvider } from "@/contexts/theme-context";
|
||||||
|
import { WebVitals } from "@/components/analytics/web-vitals";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
display: "swap",
|
||||||
|
preload: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
const geistMono = Geist_Mono({
|
||||||
variable: "--font-geist-mono",
|
variable: "--font-geist-mono",
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
display: "swap",
|
||||||
|
preload: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 思源黑体 - 中文字体
|
|
||||||
const notoSansSC = Noto_Sans_SC({
|
const notoSansSC = Noto_Sans_SC({
|
||||||
weight: ["400", "500", "700"],
|
weight: ["400", "500", "700"],
|
||||||
variable: "--font-noto-sans-sc",
|
variable: "--font-noto-sans-sc",
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
display: "swap",
|
||||||
|
preload: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -52,6 +58,16 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const viewport: Viewport = {
|
||||||
|
width: "device-width",
|
||||||
|
initialScale: 1,
|
||||||
|
maximumScale: 5,
|
||||||
|
themeColor: [
|
||||||
|
{ media: "(prefers-color-scheme: light)", color: "#FFFFFF" },
|
||||||
|
{ media: "(prefers-color-scheme: dark)", color: "#0A0A0A" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
@@ -79,6 +95,7 @@ export default function RootLayout({
|
|||||||
className={`${geistSans.variable} ${geistMono.variable} ${notoSansSC.variable} font-sans antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} ${notoSansSC.variable} font-sans antialiased`}
|
||||||
style={{ fontFamily: "'Noto Sans SC', 'Geist', -apple-system, BlinkMacSystemFont, sans-serif" }}
|
style={{ fontFamily: "'Noto Sans SC', 'Geist', -apple-system, BlinkMacSystemFont, sans-serif" }}
|
||||||
>
|
>
|
||||||
|
<WebVitals />
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
{children}
|
{children}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useReportWebVitals } from 'next/web-vitals';
|
||||||
|
|
||||||
|
export function WebVitals() {
|
||||||
|
useReportWebVitals((metric) => {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
console.log('[Web Vitals]', metric);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
const body = JSON.stringify({
|
||||||
|
name: metric.name,
|
||||||
|
value: metric.value,
|
||||||
|
rating: metric.rating,
|
||||||
|
delta: metric.delta,
|
||||||
|
id: metric.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (navigator.sendBeacon) {
|
||||||
|
navigator.sendBeacon('/api/analytics', body);
|
||||||
|
} else {
|
||||||
|
fetch('/api/analytics', {
|
||||||
|
body,
|
||||||
|
method: 'POST',
|
||||||
|
keepalive: true,
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user