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 = `

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[\\/[\\/]/,

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 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部