JavaScript性能优化实战(6):网络请求与资源加载优化
引言
在现代Web应用开发中,网络性能已成为影响用户体验的关键因素。据统计,用户等待页面加载的耐心通常不超过3秒,超过这个时间,约40%的用户会选择离开。此外,Google的研究表明,页面加载时间每增加0.5秒,流量就会下降约20%。因此,优化网络请求和资源加载不仅关乎用户体验,更直接影响业务转化率和收益。
本文将深入探讨前端网络性能优化的关键技术和策略,从性能指标、协议优化、加载策略到架构设计,全方位提升应用的网络性能。我们将结合实际案例和代码示例,展示这些优化手段如何在实战中发挥作用,帮助开发者打造高性能的Web应用。
前端网络性能指标与优化目标
核心Web指标(Core Web Vitals)
2020年,Google提出了Core Web Vitals作为衡量用户体验的关键指标,这些指标已成为业界公认的网络性能评估标准:
1. LCP (Largest Contentful Paint)
LCP测量页面主要内容加载完成的时间,直接反映页面加载速度。
// 使用Performance API监测LCP
new PerformanceObserver((entryList) => {const entries = entryList.getEntries();const lastEntry = entries[entries.length - 1];console.log('LCP:', lastEntry.startTime);console.log('LCP元素:', lastEntry.element);
}).observe({ type: 'largest-contentful-paint', buffered: true });
优化目标:良好的LCP应在2.5秒内完成
优化策略:
- 优化服务器响应时间
- 消除渲染阻塞资源
- 优化图像加载
- 实现关键CSS内联
2. FID (First Input Delay)
FID衡量用户首次与页面交互到浏览器响应的延迟时间。
// 监测FID
new PerformanceObserver((entryList) => {const entries = entryList.getEntries();entries.forEach((entry) => {console.log('FID:', entry.processingStart - entry.startTime);console.log('交互类型:', entry.name);});
}).observe({ type: 'first-input', buffered: true });
优化目标:良好的FID应在100毫秒内
优化策略:
- 拆分长任务
- 优化JavaScript执行
- 使用Web Workers处理复杂计算
- 延迟加载非关键JavaScript
3. CLS (Cumulative Layout Shift)
CLS衡量页面加载过程中的视觉稳定性,即元素意外移动的程度。
// 监测CLS
let clsValue = 0;
new PerformanceObserver((entryList) => {for (const entry of entryList.getEntries()) {if (!entry.hadRecentInput) {clsValue += entry.value;console.log('当前CLS值:', clsValue);}}
}).observe({ type: 'layout-shift', buffered: true });
优化目标:良好的CLS应小于0.1
优化策略:
- 为图像和视频指定尺寸
- 预留广告和嵌入内容的空间
- 避免在已存在内容上方插入内容
- 使用transform动画而非影响布局的属性
扩展性能指标
除了Core Web Vitals,以下指标同样重要:
1. TTFB (Time To First Byte)
TTFB测量从请求开始到收到响应第一个字节的时间。
// 测量TTFB
const navigationEntries = performance.getEntriesByType('navigation');
if (navigationEntries.length > 0) {console.log('TTFB:', navigationEntries[0].responseStart - navigationEntries[0].requestStart);
}
优化目标:良好的TTFB应在800毫秒内
2. FCP (First Contentful Paint)
FCP衡量浏览器渲染DOM中第一个内容的时间。
// 监测FCP
new PerformanceObserver((entryList) => {const entries = entryList.getEntries();entries.forEach((entry) => {console.log('FCP:', entry.startTime);});
}).observe({ type: 'paint', buffered: true });
优化目标:良好的FCP应在1.8秒内
3. TTI (Time To Interactive)
TTI测量页面变为完全可交互所需的时间。
// 需使用web-vitals库测量TTI
import { getTTI } from 'web-vitals';getTTI(console.log);
优化目标:良好的TTI应在3.8秒内
性能优化目标设定
根据不同类型的Web应用和用户场景,性能优化目标也应有所区别:
-
电商平台:
- 首屏加载时间 < 2秒
- 页面完全加载时间 < 3秒
- 交互响应时间 < 100ms
-
内容网站:
- LCP < 2秒
- 完全加载时间 < 5秒
- 图片优化率 > 80%
-
SaaS应用:
- 初始加载时间 < 3秒
- 后续操作响应时间 < 200ms
- 离线功能支持率 > 90%
-
移动Web应用:
- 首屏加载时间 < 1.5秒
- 数据传输量 < 1MB
- 低网络环境适应性测试通过率 > 95%
性能预算制定
性能预算是确保Web应用性能达标的有效工具,通常包括以下几个方面:
-
时间预算:
- 首屏加载时间不超过2秒
- API响应时间不超过200ms
- 交互响应时间不超过100ms
-
资源预算:
- JavaScript资源总量不超过300KB(压缩后)
- CSS资源总量不超过100KB(压缩后)
- 首屏图像资源不超过500KB
-
规则预算:
- Lighthouse性能得分不低于90分
- Core Web Vitals全部达到"良好"标准
- WebPageTest Speed Index不超过3000
// 使用performance-budget npm包监控性能预算
const Budget = require('performance-budget');const myBudget = new Budget({javascript: {size: 300 * 1024, // 300KBrequests: 8},css: {size: 100 * 1024, // 100KBrequests: 3},image: {size: 500 * 1024, // 500KBrequests: 15},font: {size: 100 * 1024, // 100KBrequests: 2},total: {size: 1000 * 1024, // 1MBrequests: 30,domNodes: 1500,domDepth: 20}
});// 在CI流程中检查性能预算
myBudget.check('./performance-stats.json').then(result => {if (!result.passed) {console.error('性能预算检查未通过:', result.failures);process.exit(1);}});
性能监控与分析
建立持续的性能监控体系对于优化网络性能至关重要:
- RUM (Real User Monitoring):
// 使用自定义事件上报性能数据
function sendPerformanceMetrics() {const navigationEntry = performance.getEntriesByType('navigation')[0];const paintEntries = performance.getEntriesByType('paint');const metrics = {domComplete: navigationEntry.domComplete,loadEventEnd: navigationEntry.loadEventEnd,domInteractive: navigationEntry.domInteractive,firstPaint: paintEntries.find(entry => entry.name === 'first-paint')?.startTime,firstContentfulPaint: paintEntries.find(entry => entry.name === 'first-contentful-paint')?.startTime,// 其他自定义指标};navigator.sendBeacon('/api/performance-metrics', JSON.stringify(metrics));
}window.addEventListener('unload', sendPerformanceMetrics);
- 合成监控 (Synthetic Monitoring):
使用Lighthouse、WebPageTest等工具在预定义的环境中定期测试性能。
// 使用lighthouse-ci进行自动化性能测试
// lighthouse-ci.config.js
module.exports = {ci: {collect: {url: ['https://example.com/', 'https://example.com/products'],numberOfRuns: 3,},assert: {assertions: {'categories:performance': ['error', {minScore: 0.9}],'largest-contentful-paint': ['error', {maxNumericValue: 2500}],'cumulative-layout-shift': ['error', {maxNumericValue: 0.1}],'first-input-delay': ['error', {maxNumericValue: 100}],},},upload: {target: 'lhci',serverBaseUrl: 'http://lighthouse-ci.internal/',},},
};
- 性能异常报警:
// 设置性能指标阈值并报警
function setupPerformanceAlerts() {new PerformanceObserver((entryList) => {const lcpEntry = entryList.getEntries().pop();if (lcpEntry.startTime > 4000) { // 超过4秒,发出警报sendAlert({metric: 'LCP',value: lcpEntry.startTime,url: window.location.href,timestamp: Date.now()});}}).observe({type: 'largest-contentful-paint', buffered: true});// 同样监控其他指标...
}setupPerformanceAlerts();
通过建立完善的网络性能指标体系和优化目标,我们可以有的放矢地进行优化工作,确保用户体验的持续提升。在下一节中,我们将探讨如何利用现代网络协议如HTTP/2和HTTP/3提升前端应用的网络性能。
HTTP/2与HTTP/3特性在前端的应用
随着互联网技术的发展,HTTP协议也在不断演进。从HTTP/1.1到HTTP/2再到HTTP/3,每一次升级都带来了显著的性能提升。作为前端开发者,了解并充分利用这些新协议的特性,对于优化网络请求至关重要。
HTTP/1.1的局限性
在深入HTTP/2和HTTP/3之前,我们先回顾HTTP/1.1的主要限制:
- 连接数限制:浏览器对同一域名的并行连接数有限制(通常为6-8个),导致请求队头阻塞
- 串行请求处理:HTTP/1.1中,请求需要按顺序处理,后面的请求必须等前面的请求完成
- 未压缩的头部:每个请求都会携带完整的头部信息,造成额外开销
- 简单的资源优先级:缺乏对请求优先级的细粒度控制
这些限制导致开发者采用许多变通方法,如域名分片、资源合并、雪碧图等,但这些方法往往会带来新的复杂性和维护成本。
HTTP/2特性及前端应用
HTTP/2于2015年发布,引入了多项革命性特性,极大提升了Web性能:
1. 多路复用 (Multiplexing)
HTTP/2允许在单个TCP连接上并行传输多个请求/响应,消除了HTTP/1.1的队头阻塞问题。
前端应用策略:
- 取消域名分片:不再需要将资源分散到多个域名
- 取消资源合并:可以适度拆分大型资源文件,减少缓存失效的影响范围
- 取消雪碧图:单独请求小图标不再有性能劣势
// HTTP/1.1时代的资源合并
// bundle.js包含所有JS文件
<script src="/dist/bundle.js"></script>// HTTP/2时代可以适度拆分
<script src="/js/core.js"></script>
<script src="/js/vendor.js"></script>
<script src="/js/app.js"></script>
2. 服务器推送 (Server Push)
服务器可以在客户端请求之前,主动推送可能需要的资源。
前端应用策略:
- 推送关键CSS和JavaScript:减少页面渲染阻塞时间
- 推送预计用户下一步会请求的资源:如首页推送登录页面资源
<!-- 服务器头部配置 -->
<!-- 当请求index.html时,同时推送关键CSS和JS -->
Link: </css/critical.css>; rel=preload; as=style
Link: </js/essential.js>; rel=preload; as=script
// 在Node.js服务器中实现服务器推送
const http2 = require('http2');
const fs = require('fs');const server = http2.createSecureServer({key: fs.readFileSync('server.key'),cert: fs.readFileSync('server.crt')
});server.on('stream', (stream, headers) => {const path = headers[':path'];if (path === '/') {// 推送关键资源stream.pushStream({ ':path': '/css/critical.css' }, (err, pushStream) => {if (err) throw err;pushStream.respondWithFile('./public/css/critical.css', {'content-type': 'text/css'});});stream.respondWithFile('./public/index.html', {'content-type': 'text/html'});}
});server.listen(8443);
3. 头部压缩 (HPACK)
HTTP/2引入HPACK算法压缩头部信息,减少数据传输量。
前端应用策略:
- 简化Cookie:虽有压缩,但仍应精简Cookie体积
- 利用头部压缩特性:常用头部会被高效压缩,不必过度担心头部体积
4. 二进制分帧 (Binary Framing)
HTTP/2采用二进制格式传输数据,而非HTTP/1.1的文本格式,提高了解析效率。
前端应用策略:
- 无需特殊适配:这是底层实现,前端开发者无需特别处理
5. 请求优先级 (Request Prioritization)
HTTP/2允许客户端为请求分配优先级,服务器据此优化资源传输顺序。
前端应用策略:
- 使用
<link rel="preload">
指示关键资源:浏览器会据此调整请求优先级
<!-- 使用preload标记优先资源 -->
<link rel="preload" href="/fonts/important.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/critical.css" as="style">
<link rel="preload" href="/js/app-core.js" as="script">
HTTP/3特性及前端应用
HTTP/3(基于QUIC协议)是HTTP协议的最新版本,它通过从TCP转向UDP,并在应用层实现可靠性,解决了更多性能问题:
1. 改进的连接建立
HTTP/3使用QUIC协议,将TLS握手与连接建立合并,大幅减少了连接建立时间。
前端应用策略:
- 利用0-RTT恢复:在重复访问时,几乎消除连接建立延迟
// 检测是否支持HTTP/3
const isHTTP3Supported = () => {const connection = performance.getEntriesByType('navigation')[0];return connection && connection.nextHopProtocol === 'h3';
};// 根据协议支持情况调整策略
if (isHTTP3Supported()) {// 可以更激进地拆分资源,利用HTTP/3的高效连接loadResourcesSeparately();
} else {// 回退到更保守的资源加载策略loadBundledResources();
}
2. 消除队头阻塞 (HOL Blocking)
HTTP/2虽然解决了应用层的队头阻塞,但TCP层的队头阻塞仍然存在。HTTP/3基于UDP的QUIC协议完全消除了这一问题。
前端应用策略:
- 更激进的并行请求:在HTTP/3下,可以同时发起更多并行请求
- 针对不稳定网络优化:HTTP/3在网络丢包情况下表现更佳,适合移动应用
3. 连接迁移 (Connection Migration)
HTTP/3支持在网络切换(如Wi-Fi切换到移动网络)时保持连接,而不需要重新建立。
前端应用策略:
- 优化移动应用体验:减少网络切换导致的连接中断
- 实现更可靠的长连接:如WebSocket替代方案
// 检测网络变化并通知用户HTTP/3连接维持状态
window.addEventListener('online', () => {if (isHTTP3Supported()) {console.log('网络已恢复,连接已维持');// 无需刷新页面或重新连接} else {console.log('网络已恢复,可能需要重新连接');// 提示用户刷新或自动重连}
});
4. 前向纠错 (Forward Error Correction)
HTTP/3可以在不重传的情况下恢复丢失的数据包,提高网络恢复能力。
前端应用策略:
- 高价值数据传输优化:利用HTTP/3传输关键业务数据
- 弱网环境的用户体验优化:特别适合移动设备和不稳定网络
协议支持检测与降级策略
在实际应用中,需要检测浏览器对不同HTTP协议的支持情况,并实施相应的优化策略:
// 检测HTTP协议版本
function detectHttpProtocol() {const connection = performance.getEntriesByType('navigation')[0];if (!connection || !connection.nextHopProtocol) {return 'unknown';}switch (connection.nextHopProtocol) {case 'h3':return 'HTTP/3';case 'h2':return 'HTTP/2';case 'http/1.1':return 'HTTP/1.1';default:return connection.nextHopProtocol;}
}// 根据协议实施不同策略
function applyProtocolSpecificOptimizations() {const protocol = detectHttpProtocol();switch (protocol) {case 'HTTP/3':// 充分利用HTTP/3特性enableHighParallelRequests();disableDomainSharding();optimizeForConnectionMigration();break;case 'HTTP/2':// 应用HTTP/2优化策略disableDomainSharding();enableServerPush();optimizeRequestPriority();break;case 'HTTP/1.1':// 回退到HTTP/1.1优化策略enableDomainSharding();combineResources();useSpritesAndInlining();break;default:// 使用最保守的策略applyFallbackOptimizations();}
}// 页面加载完成后应用优化
window.addEventListener('load', () => {// 等待导航性能数据可用setTimeout(() => {applyProtocolSpecificOptimizations();}, 0);
});
服务器配置最佳实践
要充分利用新协议特性,服务器端配置同样重要:
Nginx配置HTTP/2示例
server {listen 443 ssl http2;server_name example.com;ssl_certificate /path/to/cert.pem;ssl_certificate_key /path/to/key.pem;# 启用服务器推送location / {root /var/www/html;http2_push /css/critical.css;http2_push /js/essential.js;http2_push_preload on;}# 其他HTTP/2优化配置http2_max_concurrent_streams 128;http2_idle_timeout 3m;
}
Node.js HTTP/2服务器示例
const http2 = require('http2');
const fs = require('fs');
const path = require('path');const server = http2.createSecureServer({key: fs.readFileSync('server.key'),cert: fs.readFileSync('server.crt')
});server.on('stream', (stream, headers) => {const reqPath = headers[':path'];const fullPath = path.join(__dirname, 'public', reqPath);// 实现基本的静态文件服务fs.stat(fullPath, (err, stats) => {if (err) {stream.respond({ ':status': 404 });stream.end('Not Found');return;}if (reqPath === '/') {// 利用服务器推送关键资源const criticalAssets = ['/css/style.css', '/js/main.js'];criticalAssets.forEach(asset => {const assetPath = path.join(__dirname, 'public', asset);const contentType = asset.endsWith('.css') ? 'text/css' : 'application/javascript';stream.pushStream({ ':path': asset }, (err, pushStream) => {if (err) throw err;pushStream.respondWithFile(assetPath, {'content-type': contentType});});});}// 设置适当的内容类型const contentType = getContentType(reqPath);stream.respondWithFile(fullPath, {'content-type': contentType});});
});function getContentType(path) {if (path.endsWith('.html')) return 'text/html';if (path.endsWith('.css')) return 'text/css';if (path.endsWith('.js')) return 'application/javascript';if (path.endsWith('.json')) return 'application/json';if (path.endsWith('.png')) return 'image/png';if (path.endsWith('.jpg')) return 'image/jpeg';return 'text/plain';
}server.listen(8443, () => {console.log('HTTP/2服务器运行在 https://localhost:8443');
});
实际案例:电商平台HTTP/2迁移效果
某电商平台从HTTP/1.1迁移到HTTP/2后的性能提升数据:
性能指标 | HTTP/1.1 | HTTP/2 | 提升比例 |
---|---|---|---|
页面加载时间 | 3.8秒 | 2.1秒 | 45% |
首屏时间 | 2.5秒 | 1.4秒 | 44% |
资源请求数 | 35个合并请求 | 86个独立请求 | - |
总传输数据量 | 2.8MB | 2.3MB | 18% |
服务器连接数 | 8个 | 1个 | 87.5% |
迁移策略:
- 拆分过大的资源包,适度增加HTTP请求数
- 移除域名分片策略,集中到主域名
- 配置服务器推送关键CSS和JavaScript
- 移除内联资源和雪碧图
- 针对关键渲染路径资源设置优先级
通过HTTP/2和HTTP/3的高效特性,前端应用可以摆脱传统HTTP/1.1时代的许多性能优化限制,采用更自然、更易维护的资源组织方式,同时获得更好的性能表现。在下一节中,我们将探讨如何利用资源预加载与预连接技术进一步优化资源加载性能。
资源预加载与预连接技术实践
现代浏览器提供了多种资源提示(Resource Hints)技术,允许开发者指导浏览器优先获取特定资源或提前建立连接。这些技术能显著提升用户感知的加载速度,尤其是在网络条件较差的情况下。本节将深入探讨这些技术的实践应用。
资源提示(Resource Hints)概览
浏览器提供了多种资源提示机制,每种机制都有其特定用途和适用场景:
资源提示 | 作用 | 适用场景 |
---|---|---|
preload | 当前页面必须的资源预加载 | 关键CSS、字体、当前页面JS |
prefetch | 下一页面可能需要的资源预获取 | 下一页内容、用户可能操作的功能 |
preconnect | 提前建立连接(DNS+TCP+TLS) | 将从其获取资源的第三方域 |
dns-prefetch | 提前解析DNS | 后续可能访问的域名 |
prerender | 提前渲染整个页面 | 用户极可能访问的下一页面 |
preload:关键资源预加载
preload
指示浏览器尽快下载当前页面必需的资源,无论这些资源在页面中出现的位置如何。
基本用法
<!-- 预加载关键CSS -->
<link rel="preload" href="/css/critical.css" as="style"><!-- 预加载字体文件 -->
<link rel="preload" href="/fonts/roboto.woff2" as="font" type="font/woff2" crossorigin><!-- 预加载关键JavaScript -->
<link rel="preload" href="/js/important.js" as="script"><!-- 预加载JSON数据 -->
<link rel="preload" href="/api/initial-data.json" as="fetch" crossorigin><!-- 预加载主图片 -->
<link rel="preload" href="/images/hero.webp" as="image" imagesrcset="/images/hero.webp 1x, /images/hero-2x.webp 2x">
as
属性至关重要,它帮助浏览器:
- 正确设置请求优先级
- 应用正确的内容安全策略
- 设置适当的请求头(如Accept头)
动态preload
有时候,我们需要基于JavaScript逻辑动态添加preload:
// 根据用户行为动态预加载资源
function preloadResourceBasedOnUserAction(resourceUrl, type) {const link = document.createElement('link');link.rel = 'preload';link.href = resourceUrl;link.as = type; // 'style', 'script', 'font', 'image', 'fetch' 等if (type === 'font') {link.crossOrigin = 'anonymous';}document.head.appendChild(link);
}// 用户悬停在某元素上时预加载相关资源
document.querySelector('.product-card').addEventListener('mouseenter', () => {preloadResourceBasedOnUserAction('/product-details.css', 'style');preloadResourceBasedOnUserAction('/product-details.js', 'script');
});
使用HTTP头部声明preload
通过服务器响应头也可以指定预加载资源:
Link: </css/critical.css>; rel=preload; as=style
Link: </js/important.js>; rel=preload; as=script
实际案例:电子商务网站关键资源预加载
某电子商务网站通过以下预加载策略提升了首屏加载性能:
<!-- 基础样式 -->
<link rel="preload" href="/css/base.css" as="style"><!-- 产品图片尺寸占位和懒加载脚本 -->
<link rel="preload" href="/js/image-lazy-load.js" as="script"><!-- 产品卡片组件CSS -->
<link rel="preload" href="/css/product-card.css" as="style"><!-- 首屏关键产品图片 -->
<link rel="preload" href="/images/featured-product.webp" as="image"><!-- 自定义字体 -->
<link rel="preload" href="/fonts/brand-font.woff2" as="font" type="font/woff2" crossorigin><!-- 首屏产品数据 -->
<link rel="preload" href="/api/featured-products.json" as="fetch" crossorigin>
这些优化使首屏LCP从原来的2.8秒降低到1.9秒,提升了约32%的首屏加载性能。
prefetch:推测性预获取
prefetch
用于获取用户可能在不久后需要的资源,浏览器会在空闲时间低优先级地加载这些资源。
基本用法
<!-- 预获取下一页可能需要的资源 -->
<link rel="prefetch" href="/js/product-detail.js">
<link rel="prefetch" href="/css/product-detail.css"><!-- 预获取搜索结果页 -->
<link rel="prefetch" href="/api/popular-search-results.json" as="fetch" crossorigin>
动态prefetch实现
基于用户行为动态添加prefetch:
// 监测用户滚动方向和深度,预判后续可能浏览的内容
let scrollDepthThreshold = 0.3; // 滚动到30%深度时预获取下一页
let isScrollingDown = false;
let lastScrollPosition = 0;window.addEventListener('scroll', () => {const currentScrollPosition = window.scrollY;isScrollingDown = currentScrollPosition > lastScrollPosition;lastScrollPosition = currentScrollPosition;const totalHeight = document.body.scrollHeight - window.innerHeight;const scrollPercentage = currentScrollPosition / totalHeight;if (isScrollingDown && scrollPercentage > scrollDepthThreshold && !window.nextPagePrefetched) {// 预获取下一页资源const nextPageNumber = parseInt(document.querySelector('#current-page').textContent) + 1;const nextPageUrl = `/products?page=${nextPageNumber}`;const prefetchLink = document.createElement('link');prefetchLink.rel = 'prefetch';prefetchLink.href = nextPageUrl;document.head.appendChild(prefetchLink);window.nextPagePrefetched = true;console.log(`预获取下一页: ${nextPageUrl}`);}
});
预获取下一页数据
对于SPA应用,可以预获取下一页API数据:
// 在产品列表页预获取热门产品详情数据
document.querySelectorAll('.product-card').forEach(card => {card.addEventListener('mouseenter', () => {const productId = card.dataset.productId;const dataUrl = `/api/products/${productId}`;// 检查是否已经预获取if (!window.prefetchedUrls) {window.prefetchedUrls = new Set();}if (!window.prefetchedUrls.has(dataUrl)) {const link = document.createElement('link');link.rel = 'prefetch';link.href = dataUrl;link.as = 'fetch';link.crossOrigin = 'anonymous';document.head.appendChild(link);window.prefetchedUrls.add(dataUrl);console.log(`预获取产品数据: ${dataUrl}`);}});
});
路由级预获取框架集成
现代前端框架通常提供内置的路由预获取功能:
React Router示例:
import { Link, Prefetch } from 'react-router-dom';function ProductList() {return (<div className="product-grid">{products.map(product => (<div key={product.id} className="product-card"><Link to={`/product/${product.id}`}><Prefetch path={`/product/${product.id}`}><ProductCard product={product} /></Prefetch></Link></div>))}</div>);
}
Vue Router示例:
<template><div class="product-grid"><div v-for="product in products" :key="product.id" class="product-card"><router-link :to="`/product/${product.id}`" @mouseenter="prefetchProduct(product.id)"><ProductCard :product="product" /></router-link></div></div>
</template><script>
export default {methods: {prefetchProduct(id) {this.$router.prefetch(`/product/${id}`);}}
}
</script>
preconnect:提前建立连接
preconnect
提示浏览器提前建立与指定域名的连接,包括DNS解析、TCP握手和TLS协商(如果适用)。
基本用法
<!-- 预连接到CDN -->
<link rel="preconnect" href="https://cdn.example.com"><!-- 预连接到API服务器 -->
<link rel="preconnect" href="https://api.example.com"><!-- 预连接到第三方服务(如字体、分析工具) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
常见应用场景
- 字体服务优化:
<!-- Google Fonts优化加载 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
- CDN资源加载优化:
<!-- 预连接到多个CDN节点 -->
<link rel="preconnect" href="https://cdn-a.example.com">
<link rel="preconnect" href="https://cdn-b.example.com"><!-- 随后加载资源时会复用已建立的连接 -->
<script src="https://cdn-a.example.com/main.js" defer></script>
<link href="https://cdn-b.example.com/styles.css" rel="stylesheet">
- 第三方API优化:
<!-- 预连接到支付处理服务 -->
<link rel="preconnect" href="https://api.payment-processor.com"><!-- 预连接到评论系统 -->
<link rel="preconnect" href="https://comments.example-service.com">
注意事项
- 每个preconnect会占用系统资源,不应过度使用(建议不超过3-4个)
- 未在10秒内使用的preconnect连接会被关闭,浪费资源
- 应优先考虑将资源整合到更少的域名下
dns-prefetch:DNS预解析
dns-prefetch
比 preconnect
更轻量,仅执行DNS解析而不建立连接。适用于后续可能访问但不确定的资源。
基本用法
<!-- DNS预解析 -->
<link rel="dns-prefetch" href="https://possible-resource.example.com">
组合使用预连接和DNS预解析
对于同一个域,可以同时使用两种技术来兼容不同浏览器:
<!-- 现代浏览器将使用preconnect,旧浏览器回退到dns-prefetch -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
prerender:完整页面预渲染
prerender
是最重量级的资源提示,它会预先下载并渲染整个页面,包括解析HTML、执行CSS和JavaScript。
基本用法
<!-- 预渲染下一页 -->
<link rel="prerender" href="https://example.com/very-likely-next-page">
由于资源消耗大,应谨慎使用,仅用于极可能被访问的页面。
智能预渲染实现
基于用户行为预测可能访问的页面:
// 根据用户历史行为和当前页内容预测下一页面
function predictNextPage() {// 可以基于机器学习模型或简单规则const currentCategory = document.querySelector('meta[name="category"]').content;const userHistory = getUserHistory();// 简化版本:用户在当前类别停留超过30秒,预渲染该类别最热门产品if (pageViewDuration > 30 && currentCategory) {const predictedNextPage = `/products/${currentCategory}/popular`;addPrerenderHint(predictedNextPage);}
}function addPrerenderHint(url) {// 限制同时预渲染的页面数量const existingPrerender = document.querySelector('link[rel="prerender"]');if (existingPrerender) {existingPrerender.remove();}const link = document.createElement('link');link.rel = 'prerender';link.href = url;document.head.appendChild(link);console.log(`预渲染页面: ${url}`);
}// 在页面加载后开始预测
let pageLoadTime = Date.now();
window.addEventListener('load', () => {setInterval(() => {const pageViewDuration = (Date.now() - pageLoadTime) / 1000;predictNextPage();}, 5000); // 每5秒评估一次
});
实际优化案例:电子商务网站预加载策略
某电子商务网站通过全面的资源提示策略优化了用户体验:
<head><!-- 1. 关键域预连接 --><link rel="preconnect" href="https://api.store.com"><link rel="preconnect" href="https://cdn.store.com"><link rel="preconnect" href="https://images.store.com"><!-- 2. 次要域DNS预解析 --><link rel="dns-prefetch" href="https://analytics.store.com"><link rel="dns-prefetch" href="https://reviews.store.com"><!-- 3. 关键资源预加载 --><link rel="preload" href="https://cdn.store.com/css/critical.css" as="style"><link rel="preload" href="https://cdn.store.com/js/core.js" as="script"><link rel="preload" href="https://images.store.com/logo.svg" as="image"><link rel="preload" href="https://cdn.store.com/fonts/brand.woff2" as="font" type="font/woff2" crossorigin><!-- 4. 常用资源预加载 --><link rel="prefetch" href="https://cdn.store.com/js/product-page.js"><link rel="prefetch"
相关文章:
JavaScript性能优化实战(6):网络请求与资源加载优化
引言 在现代Web应用开发中,网络性能已成为影响用户体验的关键因素。据统计,用户等待页面加载的耐心通常不超过3秒,超过这个时间,约40%的用户会选择离开。此外,Google的研究表明,页面加载时间每增加0.5秒,流量就会下降约20%。因此,优化网络请求和资源加载不仅关乎用户体…...
re题(49)BUUCTF-crackMe
BUUCTF在线评测 int wmain() {FILE *v0; // eaxFILE *v1; // eaxchar v3; // [esp3h] [ebp-405h]char v4[256]; // [esp4h] [ebp-404h] BYREFchar Format[256]; // [esp104h] [ebp-304h] BYREFchar v6[256]; // [esp204h] [ebp-204h] BYREFchar v7[256]; // [esp304h] [ebp-10…...
Python中的单例模式:深入探索元类与装饰器实现
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 单例模式(Singleton Pattern)是设计模式中的一种重要模式,确保一个类在整个应用中只有一个实例,并且提供全局访问点。Python语言作为一…...
深入解析 Babylon.js 中的 TransformNode.lookAt 方法
在 3D 开发中,控制对象朝向是一个基础但关键的需求。Babylon.js 作为一款强大的 Web3D 引擎,提供了 TransformNode.lookAt 方法来实现这一功能。本文将全面解析这个方法的使用技巧、参数含义以及常见应用场景。 方法基础 TransformNode.lookAt 的基本签…...
SpringCloud组件——Gateway
一.网关 1.问题提出 我们通过Eureka,Nacos解决了服务注册,服务发现的问题,使用SpringCloud LoadBalance解决了负载均衡的问题,使用OpenFeign解决了远程调用的问题。 但是当前所有微服务的接口都是直接对外暴露的,可…...
Boost 库安装 (windows 11)
Boost 库安装 (windows 11 1 下载2 生成3 使用 1 下载 下载地址:https://www.boost.org/ 有的时候会需要历史版本下载: https://www.boost.org/users/history/ 2 生成 1、解压后点击 bootstrap.bat,会生成可执行程序b2.exe 2、双击运行b2.…...
lmms-eval--微调实战笔记
lmms-eval--大模型调用平台,方便新手上手大模型微调 lmms-eval的更多用法,没有mathversehttps://github.com/EleutherAI/lm-evaluation-harness.git 单卡运行,模型gpt-j-6B,数据集hellaswag git clone --depth 1 https://github.com/Eleuthe…...
序列密码算法ShanLooog512设计原理详解
序列密码算法ShanLooog512设计原理详解 ShanLooog512(闪龙512)为序列密码算法,内部状态为512比特,密钥长度为128或256比特,轮函数为FFFFFFFF,循环轮数为24轮,输出密钥流为512比特的状态。与Salsa20类似,内…...
Matplotlib可视化基础
1. 折线图 matplotlib.pyplot.plot() # 主要参数: x,y -- 接收array,表示X轴和Y轴对应的数据,无默认 color -- 接收特定string,指定线条的颜色,默认为None linestyle -- 接收特定string,指定线条的类型…...
Linux 内核网络协议栈中的关键数据结构:inet_skb_parm 与 ip_options
在 Linux 内核的网络协议栈中,数据包的高效处理依赖于一系列精心设计的数据结构。这些结构体不仅需要存储网络数据的元信息,还需支持复杂的协议逻辑(如路由、分片、安全策略等)。本文聚焦两个核心结构体 struct inet_skb_parm 和 struct ip_options,解析它们的设计原理、功…...
oracle 数据库查询指定用户下每个表占用空间的大小,倒序显示
oracle 查询指定用户下每个表占用空间的大小,倒序显示 使用场景:数据分析;导出医院正式库到开发环境时,查询出占用表空间高的业务表、导出时排除该表 在Oracle数据库中,要查询指定用户下每个表占用空间的大小并以倒序…...
Missashe考研日记-day29
Missashe考研日记-day29 1 专业课408 学习时间:3h学习内容: 今天先是把虚拟存储剩余的课听完了,然后就是做课后选择题,57道,已经接受了OS课后题尤其多的事实了。解决并且理解完习题之后就开始预习文件管理的内容&…...
【AI】【MCP】搭建私人王炸MCP自动化工作流
目录 一、什么是MCP 二、MCP大集合 三、准备工作 3.1 安装node.js 3.2 安装vscode 3.3 安装cline插件 3.3.1 安装 3.3.2 配置Cline 四、配置MCP服务 4.1 Search-mcp服务 4.2 playwright-mcp 服务 前言:梦想组合,轻松办公,告别手动&a…...
多元函数微分之传统方法和全微分法
一、传统方法 使用链式法则,先对中间变量(如 u,v)求偏导,再乘以中间变量对最终变量(如 x,y)的偏导。 二、全微分法 基于全微分形式不变性,直接对 zf(u,v) 求全微分 dz,再代入 du 和…...
新手SEO基础优化全解析
内容概要 对于刚接触SEO的新手而言,系统化理解优化逻辑是避免无效操作的关键。本文将从基础概念入手,逐步拆解搜索引擎排名的影响要素,围绕关键词分析、技术优化、内容策略三大核心模块,提供可落地的操作框架。通过结合工具使用说…...
MATLAB退火算法和遗传算法解决旅行商问题
模拟退火算法和遗传算法都是常用于解决旅行商问题(TSP)的优化算法,它们在原理、搜索方式、收敛速度和适用场景等方面存在一些区别: 原理 模拟退火算法:模拟退火算法的灵感来源于固体退火原理。固体在加热后缓慢冷却时…...
喜马拉雅卖身腾讯音乐:在线音频独立时代的终结
坦白说,这条消息一出来,喜马拉雅被卖掉不太奇怪,但是腾讯音乐会收购,还是有点意外。 喜马拉雅之前一度被称为中国版Audible平台,在过去几年里,活生生地把一手好牌打得稀烂。如今走到“卖身”这一步,既是无奈,也是必然。 简单回顾一下背景: 2012年,喜马拉雅成立,一…...
简单理解https与http
都是超文本传输协议,一个安全一个不安全,名字长的安全,名字短的不安全。 安全与不安全是居于什么分别的? 通过加密 http无加密。 httpshttp SSL/TSL(加密)来保障数据安全。加密传输 身份验证 SSL/TLS…...
打造即插即用的企业级云原生平台——KubeSphere 4.1 扩展组件在生产环境的价值全解
目录 打造即插即用的企业级云原生平台——KubeSphere 4.1 扩展组件在生产环境的价值全解 1. 可观测体系:WhizardTelemetry 全家桶 2. 平台与多集群治理 3. CI/CD 与交付效率 4. 网络与流量入口 5. 安全与合规 6. 存储与数据保护 7. 平台集成优势 结语 打造…...
配置扩展ACL
1.扩展ACL简介: 扩展ACL可以更精确地控制基于源IP地址、目标IP地址、协议类型和端口号的流量。 2.配置背景: 为了实现公司内部只能使用FTP服务器传输文件并关闭其他所有服务和端口的需求,可以通过配置访问控制列表(ACL…...
根据用户出生日期计算年龄
public static int calculateAgeFromDate(Date birthDate) { // 将 Date 转换为 LocalDate(默认时区) LocalDate birthLocalDate birthDate.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate(); // 获取当前日期LocalDate currentDate Local…...
测试基础笔记第十五天
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、集合1.集合的定义二、使用集合列表去重 导包二、函数1.函数介绍2.定义函数3.调用函数4.函数实现登录案例5.函数的返回值 三、模块和包1.模块的概念(Module)2.模…...
【专题四】前缀和(3)
📝前言说明: 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分每题主要记录:(1)本人解法 本人屎山代码;(2)优质解法 优质代码;ÿ…...
STM32 USB配置详解
STM32 USB配置详解 一、USB基础概念 1.1 USB简介 USB (Universal Serial Bus) 是一种用于计算机与外部设备连接的串行总线标准,具有热插拔、即插即用等特点。STM32微控制器内置了多种USB接口,可实现各类USB应用。 1.2 USB速度等级 Low Speed (LS): …...
2025年Mapbox零基础入门教程(1)地图初始化
什么是mapbox? mapbox是一个地图框架,不仅提供前端渲染能力,还具备后端服务接口能力。 相较于openlayers,它可构建二维和三维地图,并支持优化导航路线和位置查询等功能。 开发中使用mapbox需引入库文件并设置token&…...
课外知识:你需要了解的Python类对象里面的__getattr__方法
你需要了解的Python类对象中的__getattr__方法 一、__getattr__基础概念 1. 方法定义 def __getattr__(self, name: str) -> Any:"""当访问不存在的属性时触发"""2. 核心特性 动态属性处理:拦截未定义的属性访问按需触发&…...
openGauss新特性 | DataKit支持PostgreSQL到openGauss的迁移能力
Postgresql-\>openGauss迁移工具debezium-connector-postgres 可获得性 本特性自openGauss 7.0.0-RC1版本开始引入。 特性简介 debezium-connector-postgres工具是一个基于Java语言的Postgresql到openGauss的复制工具。该工具提供了初始全量数据及对象(视图、…...
【前端】跟进新趋势- PWA WebAssembly
不定期更新及补充实战。建议关注收藏点赞。 目录 PWA(渐进式 Web 应用,Progressive Web App)WebAssembly(WASM) PWA(渐进式 Web 应用,Progressive Web App) PWA 是一种提升 Web 应用…...
C++学习:六个月从基础到就业——模板编程:SFINAE原则
C学习:六个月从基础到就业——模板编程:SFINAE原则 本文是我C学习之旅系列的第三十六篇技术文章,也是第二阶段"C进阶特性"的第十四篇,主要介绍C模板编程中的SFINAE原则。查看完整系列目录了解更多内容。 目录 C学习之…...
完美解决.NET Framework 4.0 中 System.Drawing 库不支持 WebP 格式的图像处理
如果你想在 .NET Framework 4.0 中使用 ImageMagick 处理图片,可以通过 Magick.NET 库来实现。Magick.NET 是 ImageMagick 的 .NET 封装,可以用来读取、写入、编辑图像。 以下是如何使用 Magick.NET 来处理图像并提取图像的宽度和高度。 步骤ÿ…...
网络基础概念:从菜鸟到入门
前言:快递小哥的故事 想象一下你要给朋友寄个礼物,这个过程其实和网络通信非常相似: 1. 你需要知道朋友的”地址“(IP地址) 2. 要注明是送到他家大门还是物业代收(端口号) 3. 要选择快递公司并…...
优先队列和单调队列(双端队列实现的)
这里写自定义目录标题 一、优先队列与单调队列二、优先队列2.1 概念2.2 增删查 判空2.3 示例代码 三、双端队列四、单调队列4.1 单调递增队列4.2 单调递减队列 一、优先队列与单调队列 二、优先队列 2.1 概念 一种特殊的队列,它与普通队列的主要区别在于元素的出…...
设计模式(状态模式)
概述 在实际的软件开发中,状态模式并不是很常用,但是在能够用到的场景里,它可以发挥很大的作用。从这一点上来看,它有点像我们之前讲到的组合模式。 状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统…...
安卓基础(get和set)
在 Java 中,get 和 set 方法是面向对象编程中 封装(Encapsulation) 的核心实现,用于安全地访问和修改类的私有字段(private 成员变量)。它们的核心作用是 控制对数据的访问,…...
机器人灵巧操作新突破,力感知技术让机械手更精准
在机器人的发展历程中,让机器人实现灵活操作一直是科研人员努力攻克的难题。 我们这篇文章给大家带来一份新的工作:DexForce 链接:[2501.10356] DexForce: Extracting Force-informed Actions from Kinesthetic Demonstrations for Dextero…...
八大排序——直接插入排序/希尔排序
八大排序——直接插入排序/希尔排序 目录 一、直接插入排序 二、希尔排序 一、直接插入排序 每一趟从待排序序列中取第一个值,将其插入到已排序好的序列中,对已排序好的序列,从右到左依次和待插入值比较,如果大于则向后挪&…...
8、HTTPD服务--ab压力测试
一、ab压力测试 # ab ‐c 100 ‐n 1000 http://vedio.linux.com/index.html 2 This is ApacheBench, Version 2.3 <$Revision: 1430300 $> 3 Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 4 Licensed to The Apache Software Foundation,…...
node.js 实战——mongoDB
MongoDB MongoDB 简介 MongoDB 是一种基于文档型 (document-oriented) 的 NoSQL 数据库,使用类 JSON 的 BSON 格式存储数据,自然支持复杂数据结构。它特别适合需要快速变化、大量数据处理和高应用扩展性的场景。 MongoDB 特性: 无法表、无…...
C语言学习路线
以下是一份综合多个优质资源的C语言学习路线规划,结合2025年最新技术趋势和工程实践需求,分为三个阶段系统推进: 一、入门阶段(1-2个月) 目标:掌握基础语法,能编写简单程序ÿ…...
飞凌嵌入式T527核心板获得【OpenHarmony生态产品兼容性证书】
近日,飞凌嵌入式FET527-C核心板通过OpenHarmony 4.1 Release版本兼容测评,获得【OpenHarmony生态产品兼容性证书】。 飞凌嵌入式FET527-C核心板搭载全志T527系列全国产高性能处理器,集成8个ARM Cortex-A55核心,并内置RISC-V核和DS…...
Mioty|采用报文分割(Telegram Splitting)以提高抗干扰能力的无线通信技术
1、什么是Mioty 在物联网(IoT)快速发展的背景下,低功耗广域网(LPWAN)技术成为连接海量设备的关键。LPWAN具有低功耗、低成本、广覆盖和强抗干扰能力等特点,使其特别适用于大规模、远距离、低数据速率的IoT…...
WPF 程序监控硬件设备状态变化的实现方案
以下是一个完整的 C# WPF 程序实现方案,用于监控硬件设备状态变化(基于设备 SDK API)。我们将分步骤实现,包含状态轮询、事件通知、UI 绑定和错误处理。 1. 项目结构设计 HardwareMonitor/ ├── Models/ # 数据模…...
利用Python打印有符号十进制数的二进制原码、反码、补码
有时候手动计算有符号十进制数的二进制原码、反码、补码是一件挺麻烦的事情。 下面是一个段Python 代码,它可以接收一个 16 位有符号十进制数的输入,然后输出其对应的二进制原码、反码和补码: def decimal_to_binary(decimal_num):# 检查输入…...
STM32裸机编程架构与思路
STM32作为广泛应用的微控制器系列,其强大的功能和灵活的编程方式使其成为嵌入式系统开发的优选。裸机编程(bare-metal programming)指的是在没有操作系统支持的情况下,直接对硬件进行编程。这种方式虽然较为底层,但能够…...
Eureka 深度解析:从原理到部署的全场景实践
一、Eureka 核心原理与架构设计 1. 核心定位与组件模型 Eureka 是 Netflix 开源的服务发现(Service Discovery)组件,作为 Spring Cloud 微服务体系的核心基础设施,其核心目标是解决分布式系统中服务实例动态管理与跨服务通信解耦…...
有哪些和PPT自动生成有关的MCP项目?
随着AI技术的快速发展, Model Context Protocol(MCP) 作为一种连接大型语言模型(LLMs)与外部工具的开放协议,正在重塑自动化办公领域。在PPT自动生成场景中,MCP通过标准化接口实现了AI模型与设计工具、数据源的无缝整合。以下从技术框架、项目案例、应用场景三个维度展开…...
经典数仓架构深度解析与演进:从离线处理到新型架构对比
经典数仓架构深度解析与演进:从离线处理到新型架构对比 在数据驱动决策的时代,经典数仓作为企业数据管理与分析的核心基础设施,承载着从数据存储到价值挖掘的重要使命。本文将深入剖析经典数仓的架构、数据处理流程、主流架构模式及其对比&a…...
[Python开发] 如何用 VSCode 编写和管理 Python 项目(从 PyCharm 转向)
在 Python 开发领域,PyCharm 一直是广受欢迎的 IDE,但其远程开发功能(如远程 SSH 调试)仅在付费版中提供。为了适应服务器部署需求,很多开发者开始将目光转向更加轻量、灵活且免费扩展能力强的 VSCode。本篇文章将详细介绍,从 PyCharm 转向 VSCode 后,如何高效搭建和管理…...
系统架构-架构评估
质量属性 性能 指系统的响应能力 指标:响应时间、吞吐量等。 设计策略:优先级队列、增加计算资源、减少计算开销、引入并发机制、采用资源调度 可靠性 在意外或错误使用的情况下维持软件系统的功能特性 指标:MTTF、MTBF、MTTR 设计策…...
使用 MQTT - C 访问 IoTDA 平台:一个完整的嵌入式示例
引言 在物联网(IoT)开发领域,设备与平台之间的通信至关重要。MQTT 作为一种轻量级的消息传输协议,因其高效、可靠的特性,在物联网场景中得到了广泛应用。华为的 IoTDA(IoT Device Access)平台为…...