2026/3/9 2:22:40
网站建设
项目流程
a3电子报在什么网站做,做标书需要用到哪些网站查资料,如何建立网站卖东西,seo教程合集前言
上个月#xff0c;我们的产品被反馈页面加载太慢。用户在3G网络下需要等待8秒才能看到内容。
经过一个月的优化#xff0c;我们把首屏加载时间从8秒降到了1.2秒。这篇文章分享我们的优化实践。 一、性能指标体系
1.1 核心Web指标#xff08;Core Web Vi…前言上个月我们的产品被反馈页面加载太慢。用户在3G网络下需要等待8秒才能看到内容。经过一个月的优化我们把首屏加载时间从8秒降到了1.2秒。这篇文章分享我们的优化实践。一、性能指标体系1.1 核心Web指标Core Web Vitalsjavascript// LCPLargest Contentful Paint- 最大内容绘制// 目标2.5秒内const observer new PerformanceObserver((list) { list.getEntries().forEach((entry) { console.log(LCP:, entry.renderTime || entry.loadTime); });});observer.observe({entryTypes: [largest-contentful-paint]});// FIDFirst Input Delay- 首次输入延迟// 目标100毫秒内new PerformanceObserver((list) { list.getEntries().forEach((entry) { console.log(FID:, entry.processingDuration); });}).observe({entryTypes: [first-input]});// CLSCumulative Layout Shift- 累积布局偏移// 目标0.1以下let clsValue 0;new PerformanceObserver((list) { list.getEntries().forEach((entry) { if (!entry.hadRecentInput) { clsValue entry.value; console.log(CLS:, clsValue); } });}).observe({entryTypes: [layout-shift]});1.2 性能指标采集javascript// 采集所有关键指标const getMetrics () { const navigation performance.getEntriesByType(navigation)[0]; const paint performance.getEntriesByType(paint); return { // 时间相关指标 DNS: navigation.domainLookupEnd - navigation.domainLookupStart, TCP: navigation.connectEnd - navigation.connectStart, TTFB: navigation.responseStart - navigation.requestStart, // 首字节时间 DomReady: navigation.domInteractive - navigation.fetchStart, LoadComplete: navigation.loadEventEnd - navigation.fetchStart, // 渲染相关指标 FP: paint.find(p p.name first-paint)?.startTime, // 首次绘制 FCP: paint.find(p p.name first-contentful-paint)?.startTime, // 首次内容绘制 // 资源加载 Resources: performance.getEntriesByType(resource).length, };};console.log(getMetrics());二、网络优化2.1 HTTP/2与CDNjavascript// 配置CDN的关键资源const cdnConfig { // 使用CDN加速静态资源 images: https://cdn.example.com/images, scripts: https://cdn.example.com/scripts, styles: https://cdn.example.com/styles,};// 使用HTTP/2的服务器推送// 在服务器侧配置const http2Push { /index.html: [ /styles/main.css, /scripts/app.js, /fonts/roboto.woff2, ]};2.2 资源压缩bash# Gzip压缩gzip -9 static/bundle.js -c static/bundle.js.gz# Brotli压缩更高的压缩率brotli -q 11 static/bundle.js -o static/bundle.js.br# WebP图片格式cwebp original.jpg -o optimized.webp -q 802.3 缓存策略javascript// 服务器配置缓存头const express require(express);const app express();// 不变资源带hash的JS、CSS、图片app.use(/static, (req, res, next) { res.set(Cache-Control, public, max-age31536000, immutable); next();});// HTML文件始终检查新版本app.get(/, (req, res) { res.set(Cache-Control, no-cache, must-revalidate); res.sendFile(index.html);});// API响应短期缓存app.get(/api/*, (req, res) { res.set(Cache-Control, private, max-age300); // 5分钟 next();});三、代码分割与懒加载3.1 Webpack代码分割javascript// webpack.config.jsmodule.exports { optimization: { splitChunks: { chunks: all, cacheGroups: { // 分离第三方库 vendor: { test: /[\\/]node_modules[\\/]/, name: vendors, priority: 10, reuseExistingChunk: true, }, // 分离公共模块 common: { minChunks: 2, priority: 5, reuseExistingChunk: true, } } } }};3.2 动态导入与懒加载javascript// React懒加载示例import React, { Suspense, lazy } from react;const Dashboard lazy(() import(./Dashboard));const Settings lazy(() import(./Settings));function App() { return ( Suspense fallback{Loading /} Router Route path/dashboard component{Dashboard} / Route path/settings component{Settings} / /Router /Suspense );}3.3 路由懒加载javascript// Vue路由懒加载const router new VueRouter({ routes: [ { path: /home, component: () import(./views/Home.vue) }, { path: /about, component: () import(./views/About.vue) }, { path: /products, component: () import(./views/Products.vue) } ]});四、图片优化4.1 响应式图片html!-- 使用srcset适配不同屏幕 --img srcimage.jpg srcset image-320w.jpg 320w, image-640w.jpg 640w, image-1280w.jpg 1280w sizes(max-width: 320px) 280px, (max-width: 640px) 600px, 1200px altResponsive image/!-- 使用picture元素支持多种格式 --picture source srcsetimage.webp typeimage/webp / source srcsetimage.jpg typeimage/jpeg / img srcimage.jpg altFallback //picture4.2 图片懒加载javascript// 使用Intersection Observer懒加载const imageObserver new IntersectionObserver((entries, observer) { entries.forEach(entry { if (entry.isIntersecting) { const img entry.target; img.src img.dataset.src; img.classList.add(loaded); observer.unobserve(img); } });});document.querySelectorAll(img[data-src]).forEach(img { imageObserver.observe(img);});html!-- HTML使用 --img>