1. PDF资源优化策略1.1 PDF文件压缩优化图片压缩设置// 使用pdf-lib进行PDF优化 const { PDFDocument } = require('pdf-lib'); async function optimizePDF(inputPath, outputPath) { const pdfDoc = await PDFDocument.load(fs.readFileSync(inputPath)); // 获取所有页面 const pages = pdfDoc.getPages(); // 优化每个页面的图片 for (const page of pages) { const images = page.getImages(); for (const image of images) { // 压缩图片到适当尺寸 const compressedImage = await compressImage(image, { maxWidth: 1200, maxHeight: 800, quality: 85 }); page.setImage(image.ref, compressedImage); } } // 保存优化后的PDF const optimizedPdfBytes = await pdfDoc.save({ useObjectStreams: true, // 启用对象流压缩 addDefaultPage: false, objectsPerTick: 50 }); fs.writeFileSync(outputPath, optimizedPdfBytes); } // 图片压缩函数 async function compressImage(imageBuffer, options) { const sharp = require('sharp'); return await sharp(imageBuffer) .resize(options.maxWidth, options.maxHeight, { fit: 'inside', withoutEnlargement: true }) .jpeg({ quality: options.quality }) .toBuffer(); } 字体子集化// 使用fontkit进行字体子集化 const fontkit = require('fontkit'); async function subsetFont(pdfDoc, fontName, usedCharacters) { // 加载原始字体 const font = fontkit.openSync('./fonts/SourceHanSansCN-Regular.otf'); // 创建字体子集 const subset = font.createSubset(); // 添加使用到的字符 for (const char of usedCharacters) { subset.addGlyph(font.glyphForCodePoint(char.codePointAt(0))); } // 生成子集字体文件 const subsetFontBuffer = subset.encode(); // 在PDF中使用子集字体 const subsetFont = await pdfDoc.embedFont(subsetFontBuffer); return subsetFont; } 1.2 PDF分片加载服务端分片处理// Express服务端分片处理 const express = require('express'); const fs = require('fs'); const app = express(); // PDF分片下载接口 app.get('/api/pdf/:id/chunk/:chunkIndex', async (req, res) => { const { id, chunkIndex } = req.params; const chunkSize = 1024 * 1024; // 1MB分片 try { const pdfPath = `./uploads/pdfs/${id}.pdf`; const stats = fs.statSync(pdfPath); const fileSize = stats.size; const start = chunkIndex * chunkSize; const end = Math.min(start + chunkSize, fileSize); if (start >= fileSize) { return res.status(416).json({ error: 'Requested range not satisfiable' }); } const stream = fs.createReadStream(pdfPath, { start, end: end - 1 }); res.setHeader('Content-Type', 'application/pdf'); res.setHeader('Content-Range', `bytes ${start}-${end - 1}/${fileSize}`); res.setHeader('Accept-Ranges', 'bytes'); res.setHeader('Content-Length', end - start); stream.pipe(res); } catch (error) { res.status(500).json({ error: 'Failed to serve PDF chunk' }); } }); 前端分片加载// React前端分片加载组件 import React, { useState, useEffect } from 'react'; const PDFViewer = ({ pdfId }) => { const [chunks, setChunks] = useState([]); const [loadingProgress, setLoadingProgress] = useState(0); useEffect(() => { loadPDFChunks(); }, [pdfId]); const loadPDFChunks = async () => { const chunkSize = 1024 * 1024; // 1MB let chunkIndex = 0; const loadedChunks = []; while (true) { try { const response = await fetch(`/api/pdf/${pdfId}/chunk/${chunkIndex}`); if (!response.ok) { if (response.status === 416) { // 没有更多分片了 break; } throw new Error('Failed to load chunk'); } const chunk = await response.arrayBuffer(); loadedChunks.push(chunk); // 计算加载进度 const contentRange = response.headers.get('Content-Range'); const totalSize = parseInt(contentRange.split('/')[1]); const loadedSize = (chunkIndex + 1) * chunkSize; setLoadingProgress(Math.min((loadedSize / totalSize) * 100, 100)); chunkIndex++; } catch (error) { console.error('Error loading chunk:', error); break; } } // 合并所有分片 const completePDF = concatenateArrayBuffers(loadedChunks); setChunks(completePDF); }; return ( <div> <div className="progress-bar"> <div className="progress-fill" style={{ width: `${loadingProgress}%` }} /> </div> <PDFRenderer pdfData={chunks} /> </div> ); }; 1.3 PDF缓存策略CDN缓存配置// Nginx配置PDF缓存 const nginxConfig = ` # PDF文件缓存配置 location ~* \\.pdf$ { expires 30d; add_header Cache-Control "public, immutable"; add_header Vary "Accept-Encoding"; # 启用gzip压缩 gzip on; gzip_types application/pdf; gzip_min_length 1000; # 设置ETag etag on; # 启用条件请求 if_modified_since exact; } # PDF分片缓存 location ~* /api/pdf/(.+)/chunk/(.+)$ { expires 7d; add_header Cache-Control "public"; add_header X-Chunk-Index $2; } `; 浏览器缓存策略// Service Worker缓存PDF文件 self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); // PDF文件缓存策略 if (url.pathname.endsWith('.pdf')) { event.respondWith( caches.open('pdf-cache-v1').then(async (cache) => { // 先尝试从缓存获取 const cachedResponse = await cache.match(request); if (cachedResponse) { return cachedResponse; } // 缓存未命中,从网络获取 const networkResponse = await fetch(request); // 添加到缓存 if (networkResponse.ok) { cache.put(request, networkResponse.clone()); } return networkResponse; }) ); } }); 2. HTML资源优化2.1 图片懒加载优化渐进式图片加载// React图片懒加载组件 import React, { useState, useEffect, useRef } from 'react'; const ProgressiveImage = ({ src, placeholder, alt, className }) => { const [currentSrc, setCurrentSrc] = useState(placeholder); const [isLoading, setIsLoading] = useState(true); const imgRef = useRef(); useEffect(() => { // 使用Intersection Observer实现懒加载 const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { loadImage(); observer.unobserve(entry.target); } }); }, { rootMargin: '50px' // 提前50px开始加载 } ); if (imgRef.current) { observer.observe(imgRef.current); } return () => { if (imgRef.current) { observer.unobserve(imgRef.current); } }; }, []); const loadImage = () => { const img = new Image(); // 先加载低质量图片 img.src = src.replace('.jpg', '_lowq.jpg'); img.onload = () => { setCurrentSrc(img.src); // 然后加载高质量图片 const highQualityImg = new Image(); highQualityImg.src = src; highQualityImg.onload = () => { setCurrentSrc(highQualityImg.src); setIsLoading(false); }; }; }; return ( <div className={`image-container ${className}`} ref={imgRef}> <img src={currentSrc} alt={alt} className={`progressive-image ${isLoading ? 'loading' : 'loaded'}`} /> {isLoading && <div className="image-skeleton" />} </div> ); }; 响应式图片加载// 响应式图片组件 const ResponsiveImage = ({ srcSet, sizes, alt }) => { return ( <picture> {/* WebP格式 */} <source srcSet={` ${srcSet.webp400} 400w, ${srcSet.webp800} 800w, ${srcSet.webp1200} 1200w `} sizes={sizes} type="image/webp" /> {/* JPEG格式 */} <source srcSet={` ${srcSet.jpg400} 400w, ${srcSet.jpg800} 800w, ${srcSet.jpg1200} 1200w `} sizes={sizes} type="image/jpeg" /> {/* 默认图片 */} <img src={srcSet.jpg800} alt={alt} loading="lazy" decoding="async" /> </picture> ); }; 2.2 CSS和JavaScript优化关键CSS内联// 关键CSS提取和内联 const critical = require('critical'); async function extractCriticalCSS(htmlContent, cssFiles) { const { css, html } = await critical.generate({ inline: true, base: 'dist/', html: htmlContent, css: cssFiles, width: 1300, height: 900, extract: true, ignore: { atrule: ['@font-face'], rule: [/\.unused/], decl: (node, value) => { // 忽略某些CSS声明 return /print/.test(node.value); } } }); return { criticalCSS: css, optimizedHTML: html }; } JavaScript代码分割// Webpack代码分割配置 const webpackConfig = { optimization: { splitChunks: { chunks: 'all', cacheGroups: { // 第三方库 vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', priority: 10, reuseExistingChunk: true }, // 文章编辑器相关 editor: { test: /[\\/]editor[\\/]/, name: 'editor', priority: 20, reuseExistingChunk: true }, // 图表库 charts: { test: /[\\/]node_modules[\\/](chart\.js|d3|echarts)[\\/]/, name: 'charts', priority: 15, reuseExistingChunk: true }, // 通用组件 common: { minChunks: 2, priority: 5, reuseExistingChunk: true } } } } }; 2.3 字体加载优化字体预加载<!-- HTML字体预加载 --> <head> <!-- 预加载关键字体 --> <link rel="preload" href="/fonts/SourceHanSansCN-Regular.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/fonts/SourceHanSansCN-Bold.woff2" as="font" type="font/woff2" crossorigin> <!-- 字体显示策略 --> <style> @font-face { font-family: 'SourceHanSansCN'; src: url('/fonts/SourceHanSansCN-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; /* 使用备用字体,加载完成后替换 */ } @font-face { font-family: 'SourceHanSansCN'; src: url('/fonts/SourceHanSansCN-Bold.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; } </style> </head> 字体子集化// 使用glyphhanger进行字体子集化 const subsetFonts = async () => { const { exec } = require('child_process'); // 分析页面使用的字符 await exec('glyphhanger http://localhost:3000 --spider --spider-limit 10'); // 生成字体子集 await exec(` glyphhanger \ --whitelist=${usedCharacters.join(',')} \ --subset=SourceHanSansCN-Regular.woff2 \ --formats=woff2,woff \ --output=dist/fonts/ `); }; 3. 性能监控与优化3.1 性能指标监控Core Web Vitals监控// 性能监控脚本 const reportWebVitals = (onPerfEntry) => { if (onPerfEntry && onPerfEntry instanceof Function) { import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(onPerfEntry); getFID(onPerfEntry); getFCP(onPerfEntry); getLCP(onPerfEntry); getTTFB(onPerfEntry); }); } }; // 发送性能数据到分析服务 const sendAnalytics = (metric) => { // 发送到Google Analytics gtag('event', metric.name, { event_category: 'Web Vitals', event_label: metric.id, value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value), non_interaction: true }); // 发送到自定义监控服务 fetch('/api/analytics/performance', { method: 'POST', body: JSON.stringify(metric), headers: { 'Content-Type': 'application/json' } }); }; // 使用方式 reportWebVitals(sendAnalytics); 资源加载时间监控// 资源加载性能监控 const observeResourceLoading = () => { const observer = new PerformanceObserver((list) => { const entries = list.getEntries(); entries.forEach((entry) => { if (entry.entryType === 'resource') { const loadTime = entry.responseEnd - entry.startTime; const size = entry.transferSize; // 只监控重要的资源 if (shouldMonitorResource(entry.name)) { sendResourceMetrics({ name: entry.name, loadTime, size, type: entry.initiatorType, cached: entry.transferSize === 0 }); } } }); }); observer.observe({ entryTypes: ['resource'] }); }; const shouldMonitorResource = (resourceName) => { const importantResources = [ '.pdf', '.jpg', '.png', '.webp', '.js', '.css', '.woff2' ]; return importantResources.some(ext => resourceName.includes(ext)); }; 3.2 自动化性能优化图片自动压缩// 图片上传自动压缩 const processImageUpload = async (file) => { const sharp = require('sharp'); // 读取原始图片 const image = sharp(file.buffer); // 获取图片元数据 const metadata = await image.metadata(); // 根据用途生成不同尺寸 const variants = [ { suffix: '_thumb', width: 150, height: 150, quality: 80 }, { suffix: '_small', width: 400, height: 300, quality: 85 }, { suffix: '_medium', width: 800, height: 600, quality: 90 }, { suffix: '_large', width: 1200, height: 900, quality: 95 } ]; const processedImages = {}; for (const variant of variants) { const processedBuffer = await image .resize(variant.width, variant.height, { fit: 'inside', withoutEnlargement: true }) .jpeg({ quality: variant.quality }) .toBuffer(); // 上传到云存储 const fileName = `${file.name.replace(/\.[^/.]+$/, '')}${variant.suffix}.jpg`; const url = await uploadToCloudStorage(processedBuffer, fileName); processedImages[variant.suffix] = { url, width: variant.width, height: variant.height, size: processedBuffer.length }; } return processedImages; }; 资源预加载优化// 智能预加载策略 const smartPreload = () => { // 分析用户行为,预测下一步可能访问的内容 const userBehavior = analyzeUserBehavior(); // 根据用户行为预加载相关资源 if (userBehavior.intent === 'read_article') { // 预加载文章中的图片和PDF const articleResources = getArticleResources(userBehavior.targetArticle); articleResources.forEach(resource => { const link = document.createElement('link'); link.rel = 'prefetch'; link.href = resource.url; if (resource.type === 'pdf') { link.as = 'fetch'; } else if (resource.type === 'image') { link.as = 'image'; } document.head.appendChild(link); }); } // 预加载下一页内容 if (userBehavior.nextPage) { const nextPageLink = document.createElement('link'); nextPageLink.rel = 'prerender'; nextPageLink.href = userBehavior.nextPage; document.head.appendChild(nextPageLink); } }; 4. 优化效果评估4.1 性能基准测试# Lighthouse性能测试 #!/bin/bash # 运行Lighthouse测试 lighthouse http://localhost:3000 \ --output-path=./reports/lighthouse-report.html \ --chrome-flags="--headless" \ --only-categories=performance,accessibility,best-practices,seo \ --preset=desktop # 生成性能报告 node generate-performance-report.js 4.2 优化前后对比# 性能优化对比报告 ## 优化前(v1.0.0) - First Contentful Paint: 3.2s - Largest Contentful Paint: 5.1s - First Input Delay: 120ms - Cumulative Layout Shift: 0.15 - Total Blocking Time: 850ms - Speed Index: 4.8s ## 优化后(v1.1.0) - First Contentful Paint: 1.1s (-65%) - Largest Contentful Paint: 2.3s (-55%) - First Input Delay: 45ms (-62%) - Cumulative Layout Shift: 0.05 (-67%) - Total Blocking Time: 220ms (-74%) - Speed Index: 2.1s (-56%) ## 资源加载优化 - JavaScript包大小: 850KB → 420KB (-51%) - CSS文件大小: 180KB → 95KB (-47%) - 图片平均加载时间: 2.1s → 0.8s (-62%) - PDF文件加载时间: 4.5s → 1.8s (-60%) ## 用户体验改善 - 页面完全加载时间: 8.2s → 3.1s (-62%) - 移动端体验评分: 65 → 92 (+41%) - 用户跳出率: 35% → 18% (-49%) 4.3 持续优化建议定期性能审计:每月进行一次完整的性能审计A/B测试:对重大优化进行A/B测试验证效果用户反馈收集:收集用户对加载速度的反馈新技术跟进:关注新的优化技术和工具竞品对比:定期对比竞品的性能表现

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部
2.210870s