Search K
Appearance
Appearance
关键词: 关键渲染路径, 资源压缩, 缓存策略, 延迟加载, 预加载技术, 性能优化, 加载时间, 用户体验
关键渲染路径是浏览器从接收HTML、CSS和JavaScript到渲染出像素画面的过程。优化这个过程是提升页面性能的关键。
<!-- 优化关键渲染路径的HTML结构 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面加载优化示例</title>
<!-- 关键CSS内联 -->
<style>
/* 首屏关键样式 */
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 0;
line-height: 1.6;
}
.header {
background: #2c3e50;
color: white;
padding: 1rem;
text-align: center;
}
.loading-placeholder {
height: 200px;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
color: #666;
}
/* 首屏加载动画 */
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<!-- 预加载关键资源 -->
<link rel="preload" href="critical-font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="hero-image.jpg" as="image">
<!-- 预连接到外部域名 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://api.example.com">
</head>
<body>
<header class="header">
<h1>性能优化示例</h1>
</header>
<main>
<!-- 首屏内容 -->
<section class="hero">
<div class="loading-placeholder">
<div class="loader"></div>
<span>加载中...</span>
</div>
</section>
<!-- 非关键内容使用懒加载 -->
<section class="content" data-lazy-load>
<!-- 内容将通过JavaScript延迟加载 -->
</section>
</main>
<!-- 非关键CSS延迟加载 -->
<link rel="preload" href="non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="non-critical.css"></noscript>
<!-- JavaScript放在页面底部 -->
<script>
// 关键JavaScript内联
(function() {
// 页面性能监控
window.addEventListener('load', function() {
const perfData = performance.timing;
const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
console.log('页面加载时间:', pageLoadTime + 'ms');
});
// 延迟加载非关键内容
function loadNonCriticalContent() {
const lazyElements = document.querySelectorAll('[data-lazy-load]');
lazyElements.forEach(element => {
// 加载内容的逻辑
element.innerHTML = '<p>延迟加载的内容已显示</p>';
});
}
// 页面加载完成后延迟加载
window.addEventListener('load', function() {
setTimeout(loadNonCriticalContent, 100);
});
})();
</script>
</body>
</html><!-- 关键渲染路径优化示例 -->
<div class="critical-path-optimization">
<h3>关键渲染路径优化策略</h3>
<div class="strategy-grid">
<div class="strategy-card">
<h4>1. 减少关键资源数量</h4>
<div class="example">
<p>将关键CSS内联到HTML中:</p>
<pre><code><style>
/* 首屏关键样式 */
.above-fold { ... }
</style></code></pre>
</div>
</div>
<div class="strategy-card">
<h4>2. 减少关键字节数</h4>
<div class="example">
<p>压缩CSS和JavaScript:</p>
<pre><code>/* 压缩前 */
.header {
background-color: #ffffff;
padding: 20px;
}
/* 压缩后 */
.header{background:#fff;padding:20px}</code></pre>
</div>
</div>
<div class="strategy-card">
<h4>3. 优化关键路径长度</h4>
<div class="example">
<p>减少CSS中的@import使用:</p>
<pre><code><!-- 避免 -->
<style>@import url('styles.css');</style>
<!-- 推荐 -->
<link rel="stylesheet" href="styles.css"></code></pre>
</div>
</div>
</div>
</div>
<style>
.critical-path-optimization {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.critical-path-optimization h3 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.strategy-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 30px;
}
.strategy-card {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border-left: 4px solid #3498db;
}
.strategy-card h4 {
color: #2c3e50;
margin: 0 0 15px 0;
}
.example {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin-top: 10px;
}
.example p {
color: #666;
margin: 0 0 10px 0;
font-size: 14px;
}
.example pre {
margin: 0;
overflow-x: auto;
}
.example code {
background: #2c3e50;
color: #ecf0f1;
padding: 10px;
border-radius: 4px;
display: block;
font-size: 12px;
line-height: 1.4;
}
</style><!-- HTML压缩示例 -->
<div class="html-compression">
<h3>HTML压缩技术</h3>
<div class="compression-comparison">
<div class="before-compression">
<h4>压缩前的HTML</h4>
<pre><code><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>示例页面</title>
<!-- 这是一个注释 -->
<style>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: #333333;
color: #ffffff;
padding: 1rem;
}
</style>
</head>
<body>
<div class="container">
<header class="header">
<h1>页面标题</h1>
</header>
<main>
<p>页面内容</p>
</main>
</div>
</body>
</html></code></pre>
</div>
<div class="after-compression">
<h4>压缩后的HTML</h4>
<pre><code><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>示例页面</title><style>.container{max-width:1200px;margin:0 auto;padding:20px}.header{background-color:#333;color:#fff;padding:1rem}</style></head><body><div class="container"><header class="header"><h1>页面标题</h1></header><main><p>页面内容</p></main></div></body></html></code></pre>
</div>
</div>
<div class="compression-benefits">
<h4>压缩效果</h4>
<div class="benefits-grid">
<div class="benefit-item">
<span class="benefit-label">文件大小减少</span>
<span class="benefit-value">~30-40%</span>
</div>
<div class="benefit-item">
<span class="benefit-label">传输时间减少</span>
<span class="benefit-value">~25-35%</span>
</div>
<div class="benefit-item">
<span class="benefit-label">带宽节省</span>
<span class="benefit-value">显著</span>
</div>
</div>
</div>
</div>
<style>
.html-compression {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.html-compression h3 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.compression-comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.before-compression,
.after-compression {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.before-compression h4 {
color: #e74c3c;
margin: 0 0 15px 0;
}
.after-compression h4 {
color: #27ae60;
margin: 0 0 15px 0;
}
.compression-comparison pre {
margin: 0;
overflow-x: auto;
}
.compression-comparison code {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 8px;
display: block;
font-size: 11px;
line-height: 1.4;
white-space: pre-wrap;
word-break: break-all;
}
.compression-benefits {
background: #f8f9fa;
padding: 20px;
border-radius: 12px;
}
.compression-benefits h4 {
color: #333;
margin: 0 0 15px 0;
text-align: center;
}
.benefits-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.benefit-item {
text-align: center;
padding: 15px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.benefit-label {
display: block;
color: #666;
font-size: 14px;
margin-bottom: 5px;
}
.benefit-value {
display: block;
color: #27ae60;
font-weight: bold;
font-size: 18px;
}
@media (max-width: 768px) {
.compression-comparison {
grid-template-columns: 1fr;
}
.benefits-grid {
grid-template-columns: 1fr;
}
}
</style><!-- CSS和JavaScript压缩示例 -->
<div class="css-js-compression">
<h3>CSS和JavaScript压缩</h3>
<div class="compression-techniques">
<div class="technique-card">
<h4>CSS压缩技术</h4>
<div class="technique-example">
<div class="code-before">
<h5>压缩前:</h5>
<pre><code>/* 按钮样式 */
.button {
background-color: #3498db;
border: none;
border-radius: 4px;
color: #ffffff;
cursor: pointer;
display: inline-block;
font-family: Arial, sans-serif;
font-size: 16px;
font-weight: 600;
line-height: 1.5;
margin: 0;
padding: 12px 24px;
text-align: center;
text-decoration: none;
transition: background-color 0.3s ease;
user-select: none;
vertical-align: middle;
white-space: nowrap;
}
.button:hover {
background-color: #2980b9;
}
.button:active {
background-color: #1e6a96;
}</code></pre>
</div>
<div class="code-after">
<h5>压缩后:</h5>
<pre><code>.button{background-color:#3498db;border:none;border-radius:4px;color:#fff;cursor:pointer;display:inline-block;font-family:Arial,sans-serif;font-size:16px;font-weight:600;line-height:1.5;margin:0;padding:12px 24px;text-align:center;text-decoration:none;transition:background-color .3s ease;user-select:none;vertical-align:middle;white-space:nowrap}.button:hover{background-color:#2980b9}.button:active{background-color:#1e6a96}</code></pre>
</div>
</div>
</div>
<div class="technique-card">
<h4>JavaScript压缩技术</h4>
<div class="technique-example">
<div class="code-before">
<h5>压缩前:</h5>
<pre><code>// 图片懒加载功能
function initLazyLoading() {
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
const image = entry.target;
image.src = image.getAttribute('data-src');
image.removeAttribute('data-src');
imageObserver.unobserve(image);
}
});
});
images.forEach(function(image) {
imageObserver.observe(image);
});
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initLazyLoading();
});</code></pre>
</div>
<div class="code-after">
<h5>压缩后:</h5>
<pre><code>function initLazyLoading(){const a=document.querySelectorAll("img[data-src]"),b=new IntersectionObserver(function(c){c.forEach(function(d){if(d.isIntersecting){const e=d.target;e.src=e.getAttribute("data-src"),e.removeAttribute("data-src"),b.unobserve(e)}})});a.forEach(function(c){b.observe(c)})}document.addEventListener("DOMContentLoaded",function(){initLazyLoading()});</code></pre>
</div>
</div>
</div>
</div>
<div class="compression-tools">
<h4>常用压缩工具</h4>
<div class="tools-grid">
<div class="tool-item">
<h5>在线工具</h5>
<ul>
<li>CSS Minifier</li>
<li>JavaScript Minifier</li>
<li>HTML Minifier</li>
</ul>
</div>
<div class="tool-item">
<h5>构建工具</h5>
<ul>
<li>Webpack</li>
<li>Gulp</li>
<li>Parcel</li>
</ul>
</div>
<div class="tool-item">
<h5>压缩库</h5>
<ul>
<li>UglifyJS</li>
<li>Terser</li>
<li>cssnano</li>
</ul>
</div>
</div>
</div>
</div>
<style>
.css-js-compression {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.css-js-compression h3 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.compression-techniques {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 40px;
}
.technique-card {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.technique-card h4 {
color: #2c3e50;
margin: 0 0 20px 0;
text-align: center;
}
.code-before,
.code-after {
margin-bottom: 20px;
}
.code-before h5 {
color: #e74c3c;
margin: 0 0 10px 0;
}
.code-after h5 {
color: #27ae60;
margin: 0 0 10px 0;
}
.technique-example pre {
margin: 0;
overflow-x: auto;
}
.technique-example code {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 8px;
display: block;
font-size: 11px;
line-height: 1.4;
white-space: pre-wrap;
}
.compression-tools {
background: #f8f9fa;
padding: 25px;
border-radius: 12px;
}
.compression-tools h4 {
color: #333;
margin: 0 0 20px 0;
text-align: center;
}
.tools-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.tool-item {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.tool-item h5 {
color: #3498db;
margin: 0 0 15px 0;
text-align: center;
}
.tool-item ul {
list-style: none;
padding: 0;
margin: 0;
}
.tool-item li {
color: #666;
padding: 5px 0;
border-bottom: 1px solid #eee;
}
.tool-item li:last-child {
border-bottom: none;
}
@media (max-width: 768px) {
.compression-techniques {
grid-template-columns: 1fr;
}
.tools-grid {
grid-template-columns: 1fr;
}
}
</style><!-- HTTP缓存策略示例 -->
<div class="http-caching">
<h3>HTTP缓存策略</h3>
<div class="cache-headers">
<div class="header-example">
<h4>Cache-Control头部设置</h4>
<div class="header-code">
<pre><code># 静态资源缓存(1年)
Cache-Control: public, max-age=31536000, immutable
# HTML文件缓存策略
Cache-Control: public, max-age=0, must-revalidate
# API响应缓存
Cache-Control: private, max-age=300
# 不缓存敏感数据
Cache-Control: no-cache, no-store, must-revalidate</code></pre>
</div>
</div>
<div class="header-example">
<h4>ETag和Last-Modified</h4>
<div class="header-code">
<pre><code># 服务器响应头
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
# 客户端请求头
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT</code></pre>
</div>
</div>
</div>
<div class="cache-strategy-table">
<h4>缓存策略参考</h4>
<table class="strategy-table">
<thead>
<tr>
<th>资源类型</th>
<th>缓存策略</th>
<th>缓存时间</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>HTML</td>
<td>no-cache</td>
<td>0</td>
<td>每次验证是否更新</td>
</tr>
<tr>
<td>CSS/JS</td>
<td>public, immutable</td>
<td>1年</td>
<td>使用版本号控制更新</td>
</tr>
<tr>
<td>图片</td>
<td>public</td>
<td>30天</td>
<td>较长时间缓存</td>
</tr>
<tr>
<td>字体</td>
<td>public, immutable</td>
<td>1年</td>
<td>很少变化的资源</td>
</tr>
<tr>
<td>API数据</td>
<td>private</td>
<td>5-15分钟</td>
<td>用户相关数据</td>
</tr>
</tbody>
</table>
</div>
</div>
<style>
.http-caching {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.http-caching h3 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.cache-headers {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 40px;
}
.header-example {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.header-example h4 {
color: #2c3e50;
margin: 0 0 15px 0;
}
.header-code pre {
margin: 0;
overflow-x: auto;
}
.header-code code {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 8px;
display: block;
font-size: 12px;
line-height: 1.4;
}
.cache-strategy-table {
background: #f8f9fa;
padding: 25px;
border-radius: 12px;
}
.cache-strategy-table h4 {
color: #333;
margin: 0 0 20px 0;
text-align: center;
}
.strategy-table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.strategy-table th {
background: #3498db;
color: white;
padding: 15px;
text-align: left;
font-weight: 600;
}
.strategy-table td {
padding: 12px 15px;
border-bottom: 1px solid #eee;
color: #333;
}
.strategy-table tr:last-child td {
border-bottom: none;
}
.strategy-table tr:nth-child(even) {
background: #f8f9fa;
}
@media (max-width: 768px) {
.cache-headers {
grid-template-columns: 1fr;
}
.strategy-table {
font-size: 14px;
}
.strategy-table th,
.strategy-table td {
padding: 10px 8px;
}
}
</style><!-- Service Worker缓存示例 -->
<div class="service-worker-cache">
<h3>Service Worker缓存</h3>
<div class="sw-implementation">
<div class="sw-code">
<h4>Service Worker注册</h4>
<pre><code>// 主线程中注册Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log('SW注册成功:', registration.scope);
})
.catch(function(error) {
console.log('SW注册失败:', error);
});
});
}</code></pre>
</div>
<div class="sw-code">
<h4>Service Worker缓存策略</h4>
<pre><code>// sw.js - Service Worker文件
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/main.js',
'/images/logo.png'
];
// 安装事件 - 缓存资源
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
return cache.addAll(urlsToCache);
})
);
});
// 网络请求拦截
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// 缓存命中,返回缓存资源
if (response) {
return response;
}
// 缓存未命中,发起网络请求
return fetch(event.request)
.then(function(response) {
// 检查是否是有效响应
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 克隆响应,因为响应流只能使用一次
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});</code></pre>
</div>
</div>
<div class="cache-strategies">
<h4>缓存策略模式</h4>
<div class="strategies-grid">
<div class="strategy-item">
<h5>Cache First</h5>
<p>优先从缓存读取,适用于不经常变化的静态资源</p>
<div class="strategy-code">
<pre><code>// 缓存优先策略
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})</code></pre>
</div>
</div>
<div class="strategy-item">
<h5>Network First</h5>
<p>优先从网络获取,适用于经常变化的动态内容</p>
<div class="strategy-code">
<pre><code>// 网络优先策略
fetch(event.request)
.catch(() => {
return caches.match(event.request);
})</code></pre>
</div>
</div>
<div class="strategy-item">
<h5>Stale While Revalidate</h5>
<p>返回缓存的同时更新缓存,适用于可接受过期数据的场景</p>
<div class="strategy-code">
<pre><code>// 过期重新验证策略
const cachedResponse = caches.match(event.request);
const fetchPromise = fetch(event.request).then(response => {
cache.put(event.request, response.clone());
return response;
});
return cachedResponse || fetchPromise;</code></pre>
</div>
</div>
</div>
</div>
</div>
<style>
.service-worker-cache {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.service-worker-cache h3 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.sw-implementation {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 40px;
}
.sw-code {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.sw-code h4 {
color: #2c3e50;
margin: 0 0 15px 0;
}
.sw-code pre {
margin: 0;
overflow-x: auto;
}
.sw-code code {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 8px;
display: block;
font-size: 11px;
line-height: 1.4;
}
.cache-strategies {
background: #f8f9fa;
padding: 25px;
border-radius: 12px;
}
.cache-strategies h4 {
color: #333;
margin: 0 0 20px 0;
text-align: center;
}
.strategies-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.strategy-item {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.strategy-item h5 {
color: #3498db;
margin: 0 0 10px 0;
}
.strategy-item p {
color: #666;
font-size: 14px;
margin: 0 0 15px 0;
line-height: 1.5;
}
.strategy-code pre {
margin: 0;
overflow-x: auto;
}
.strategy-code code {
background: #2c3e50;
color: #ecf0f1;
padding: 10px;
border-radius: 4px;
display: block;
font-size: 10px;
line-height: 1.3;
}
@media (max-width: 768px) {
.sw-implementation {
grid-template-columns: 1fr;
}
.strategies-grid {
grid-template-columns: 1fr;
}
}
</style><!-- 延迟加载实现示例 -->
<div class="lazy-loading-implementation">
<h3>延迟加载技术</h3>
<div class="lazy-examples">
<div class="image-lazy-loading">
<h4>图片延迟加载</h4>
<div class="image-gallery">
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200'%3E%3Crect width='100%25' height='100%25' fill='%23f0f0f0'/%3E%3Ctext x='50%25' y='50%25' text-anchor='middle' dy='.3em' fill='%23666'%3E加载中...%3C/text%3E%3C/svg%3E"
data-src="https://picsum.photos/300/200?random=1"
class="lazy-img"
alt="延迟加载图片1">
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200'%3E%3Crect width='100%25' height='100%25' fill='%23f0f0f0'/%3E%3Ctext x='50%25' y='50%25' text-anchor='middle' dy='.3em' fill='%23666'%3E加载中...%3C/text%3E%3C/svg%3E"
data-src="https://picsum.photos/300/200?random=2"
class="lazy-img"
alt="延迟加载图片2">
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='200'%3E%3Crect width='100%25' height='100%25' fill='%23f0f0f0'/%3E%3Ctext x='50%25' y='50%25' text-anchor='middle' dy='.3em' fill='%23666'%3E加载中...%3C/text%3E%3C/svg%3E"
data-src="https://picsum.photos/300/200?random=3"
class="lazy-img"
alt="延迟加载图片3">
</div>
<div class="lazy-code">
<h5>实现代码:</h5>
<pre><code>// Intersection Observer API实现图片懒加载
function initImageLazyLoading() {
const images = document.querySelectorAll('img[data-src]');
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('loaded');
imageObserver.unobserve(img);
}
});
}, {
rootMargin: '50px 0px',
threshold: 0.01
});
images.forEach(img => imageObserver.observe(img));
} else {
// 降级方案:直接加载所有图片
images.forEach(img => {
img.src = img.dataset.src;
img.removeAttribute('data-src');
});
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', initImageLazyLoading);</code></pre>
</div>
</div>
<div class="content-lazy-loading">
<h4>内容延迟加载</h4>
<div class="content-sections">
<div class="content-section visible">
<h5>首屏内容</h5>
<p>这是首屏显示的内容,会立即加载。</p>
</div>
<div class="content-section" data-lazy-content>
<div class="loading-placeholder">
<div class="loading-spinner"></div>
<span>内容加载中...</span>
</div>
</div>
<div class="content-section" data-lazy-content>
<div class="loading-placeholder">
<div class="loading-spinner"></div>
<span>内容加载中...</span>
</div>
</div>
</div>
<div class="lazy-code">
<h5>内容延迟加载实现:</h5>
<pre><code>// 内容延迟加载
function initContentLazyLoading() {
const sections = document.querySelectorAll('[data-lazy-content]');
const sectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadSectionContent(entry.target);
sectionObserver.unobserve(entry.target);
}
});
}, {
rootMargin: '100px 0px',
threshold: 0.1
});
sections.forEach(section => sectionObserver.observe(section));
}
function loadSectionContent(section) {
// 模拟异步加载内容
setTimeout(() => {
section.innerHTML = `
<h5>延迟加载的内容</h5>
<p>这部分内容在用户滚动到附近时才加载,提高了页面初始加载速度。</p>
`;
section.classList.add('loaded');
}, 1000);
}</code></pre>
</div>
</div>
</div>
</div>
<style>
.lazy-loading-implementation {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.lazy-loading-implementation h3 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.lazy-examples {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
}
.image-lazy-loading,
.content-lazy-loading {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.image-lazy-loading h4,
.content-lazy-loading h4 {
color: #2c3e50;
margin: 0 0 20px 0;
text-align: center;
}
.image-gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-bottom: 20px;
}
.lazy-img {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 8px;
transition: opacity 0.3s ease;
opacity: 0.7;
}
.lazy-img.loaded {
opacity: 1;
}
.content-sections {
margin-bottom: 20px;
}
.content-section {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 15px;
min-height: 80px;
}
.content-section.visible {
background: #e3f2fd;
}
.content-section.loaded {
background: #e8f5e8;
animation: fadeIn 0.5s ease;
}
.loading-placeholder {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
color: #666;
}
.loading-spinner {
width: 20px;
height: 20px;
border: 2px solid #f3f3f3;
border-top: 2px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.lazy-code {
background: #2c3e50;
border-radius: 8px;
overflow: hidden;
}
.lazy-code h5 {
color: #ecf0f1;
margin: 0;
padding: 15px 15px 0;
}
.lazy-code pre {
margin: 0;
overflow-x: auto;
}
.lazy-code code {
color: #ecf0f1;
padding: 15px;
display: block;
font-size: 11px;
line-height: 1.4;
}
@media (max-width: 768px) {
.lazy-examples {
grid-template-columns: 1fr;
}
.image-gallery {
grid-template-columns: 1fr;
}
}
</style>
<script>
// 图片懒加载实现
function initImageLazyLoading() {
const images = document.querySelectorAll('img[data-src]');
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('loaded');
imageObserver.unobserve(img);
}
});
}, {
rootMargin: '50px 0px',
threshold: 0.01
});
images.forEach(img => imageObserver.observe(img));
} else {
// 降级方案
images.forEach(img => {
img.src = img.dataset.src;
img.removeAttribute('data-src');
});
}
}
// 内容懒加载实现
function initContentLazyLoading() {
const sections = document.querySelectorAll('[data-lazy-content]');
const sectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadSectionContent(entry.target);
sectionObserver.unobserve(entry.target);
}
});
}, {
rootMargin: '100px 0px',
threshold: 0.1
});
sections.forEach(section => sectionObserver.observe(section));
}
function loadSectionContent(section) {
setTimeout(() => {
section.innerHTML = `
<h5>延迟加载的内容</h5>
<p>这部分内容在用户滚动到附近时才加载,提高了页面初始加载速度。</p>
`;
section.classList.add('loaded');
}, 1000);
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initImageLazyLoading();
initContentLazyLoading();
});
</script><!-- 预加载技术示例 -->
<div class="preloading-techniques">
<h3>预加载技术</h3>
<div class="preload-methods">
<div class="method-card">
<h4>DNS预解析</h4>
<div class="method-example">
<pre><code><!-- DNS预解析 -->
<link rel="dns-prefetch" href="//fonts.googleapis.com">
<link rel="dns-prefetch" href="//api.example.com">
<link rel="dns-prefetch" href="//cdn.example.com"></code></pre>
</div>
<p>提前解析域名,减少DNS查询时间。</p>
</div>
<div class="method-card">
<h4>预连接</h4>
<div class="method-example">
<pre><code><!-- 预连接 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://api.example.com"></code></pre>
</div>
<p>提前建立连接,包括DNS解析、TCP握手和TLS协商。</p>
</div>
<div class="method-card">
<h4>资源预加载</h4>
<div class="method-example">
<pre><code><!-- 预加载关键资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero-image.jpg" as="image">
<link rel="preload" href="main-font.woff2" as="font" type="font/woff2" crossorigin></code></pre>
</div>
<p>预加载当前页面需要的关键资源。</p>
</div>
<div class="method-card">
<h4>页面预取</h4>
<div class="method-example">
<pre><code><!-- 预取下一页面的资源 -->
<link rel="prefetch" href="/next-page.html">
<link rel="prefetch" href="/images/next-page-hero.jpg"></code></pre>
</div>
<p>预取用户可能访问的下一个页面资源。</p>
</div>
</div>
<div class="preload-javascript">
<h4>JavaScript预加载实现</h4>
<div class="js-preload-example">
<pre><code>// JavaScript预加载实现
class ResourcePreloader {
constructor() {
this.preloadQueue = new Map();
this.loadedResources = new Set();
}
// 预加载图片
preloadImage(src, priority = 'low') {
if (this.loadedResources.has(src)) {
return Promise.resolve();
}
if (this.preloadQueue.has(src)) {
return this.preloadQueue.get(src);
}
const promise = new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
this.loadedResources.add(src);
resolve(img);
};
img.onerror = reject;
img.src = src;
});
this.preloadQueue.set(src, promise);
return promise;
}
// 预加载CSS
preloadCSS(href) {
if (this.loadedResources.has(href)) {
return Promise.resolve();
}
const promise = new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'style';
link.href = href;
link.onload = () => {
this.loadedResources.add(href);
resolve();
};
link.onerror = reject;
document.head.appendChild(link);
});
this.preloadQueue.set(href, promise);
return promise;
}
// 预加载脚本
preloadScript(src) {
if (this.loadedResources.has(src)) {
return Promise.resolve();
}
const promise = new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'script';
link.href = src;
link.onload = () => {
this.loadedResources.add(src);
resolve();
};
link.onerror = reject;
document.head.appendChild(link);
});
this.preloadQueue.set(src, promise);
return promise;
}
// 智能预加载:基于用户行为
intelligentPreload() {
// 监听鼠标悬停事件
document.addEventListener('mouseover', (e) => {
const link = e.target.closest('a[href]');
if (link && link.hostname === location.hostname) {
this.preloadPage(link.href);
}
});
// 监听链接可见性
if ('IntersectionObserver' in window) {
const linkObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const link = entry.target;
if (link.hostname === location.hostname) {
this.preloadPage(link.href);
}
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('a[href]').forEach(link => {
linkObserver.observe(link);
});
}
}
// 预加载页面
preloadPage(url) {
if (this.loadedResources.has(url)) {
return;
}
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
document.head.appendChild(link);
this.loadedResources.add(url);
}
}
// 使用示例
const preloader = new ResourcePreloader();
// 预加载关键资源
preloader.preloadImage('/images/hero.jpg')
.then(() => console.log('英雄图片预加载完成'));
preloader.preloadCSS('/styles/non-critical.css');
// 启用智能预加载
preloader.intelligentPreload();</code></pre>
</div>
</div>
</div>
<style>
.preloading-techniques {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.preloading-techniques h3 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.preload-methods {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 40px;
}
.method-card {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border-left: 4px solid #3498db;
}
.method-card h4 {
color: #2c3e50;
margin: 0 0 15px 0;
}
.method-example {
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 15px;
overflow: hidden;
}
.method-example pre {
margin: 0;
overflow-x: auto;
}
.method-example code {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
display: block;
font-size: 11px;
line-height: 1.4;
}
.method-card p {
color: #666;
margin: 0;
font-size: 14px;
line-height: 1.5;
}
.preload-javascript {
background: #f8f9fa;
padding: 25px;
border-radius: 12px;
}
.preload-javascript h4 {
color: #333;
margin: 0 0 20px 0;
text-align: center;
}
.js-preload-example {
background: #2c3e50;
border-radius: 8px;
overflow: hidden;
}
.js-preload-example pre {
margin: 0;
overflow-x: auto;
}
.js-preload-example code {
color: #ecf0f1;
padding: 20px;
display: block;
font-size: 11px;
line-height: 1.4;
}
@media (max-width: 768px) {
.preload-methods {
grid-template-columns: 1fr;
}
}
</style>A: 关键渲染路径是浏览器显示首屏内容所需的最小资源集合。优化它可以显著提升首屏渲染时间,改善用户体验。
A: 根据资源特性选择:静态资源使用长期缓存;动态内容使用短期缓存或不缓存;API数据根据更新频率设置合适的缓存时间。
A: 正确实现的延迟加载不会影响SEO。确保为图片提供alt属性,为延迟加载的内容提供合适的占位符,必要时提供noscript降级方案。
A: 预加载(preload)用于当前页面的关键资源;预取(prefetch)用于下一个页面可能需要的资源。预加载优先级高,预取优先级低。
A: HTTP缓存由浏览器自动管理,缓存策略相对固定;Service Worker缓存由开发者编程控制,可以实现更复杂的缓存策略和离线功能。
下一节预览:下一节我们将学习代码优化,重点介绍HTML代码优化、标签使用最佳实践、文档结构优化、无用代码清理和代码压缩的具体方法。