快乐大罗山 發表於 2024-12-14 08:51:46

从加载到渲染的全链路提速的网站打开速度前端优化

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">性能问题诊断</a></li><li><a href="#_label1">加载优化</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">1. 资源加载策略优化</a></li><li><a href="#_lab2_1_1">2. 图片优化</a></li><li><a href="#_lab2_1_2">3. 代码分割和懒加载</a></li></ul><li><a href="#_label2">渲染优化</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_3">1. 虚拟列表实现</a></li><li><a href="#_lab2_2_4">2. 状态管理优化</a></li><li><a href="#_lab2_2_5">3. 缓存策略</a></li></ul><li><a href="#_label3">性能监控</a></li><ul class="second_class_ul"></ul><li><a href="#_label4">优化成果</a></li><ul class="second_class_ul"></ul><li><a href="#_label5">写在最后</a></li><ul class="second_class_ul"></ul></ul></div><p>&quot;网站太慢了,用户都在抱怨!&quot;上周,我接手了一个正在运行的电商网站,首屏加载时间竟然长达 8 秒。作为一个对性能有执念的前端开发者,这个数字让我夜不能寐。经过一周的优化,我们把首屏时间压缩到了 2 秒以内。今天,我想和大家分享网站打开速度前端优化的实战经验。😊</p>
<p class="maodian"><a name="_label0"></a></p><h2>性能问题诊断</h2>
<p>首先,我们需要找出性能瓶颈在哪里。通过 Chrome DevTools 的 Performance 和 Network 面板,我发现了几个主要问题:</p>
<div class="jb51code"><pre class="brush:plain;">// 问题1:资源加载顺序不合理
// 之前的代码
&lt;head&gt;
&lt;link rel="stylesheet" href="/styles/main.css" rel="external nofollow"rel="external nofollow" &gt;
&lt;script src="/js/analytics.js"&gt;&lt;/script&gt;
&lt;script src="/js/main.js"&gt;&lt;/script&gt;
&lt;/head&gt;

// 问题2:图片资源没有优化
&lt;img src="large-product-image.jpg" alt="product"&gt;

// 问题3:大量的同步 JavaScript 执行
window.onload = function() {
initializeEverything();
setupEventListeners();
loadThirdPartyScripts();
}
</pre></div>
<p class="maodian"><a name="_label1"></a></p><h2>加载优化</h2>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>1. 资源加载策略优化</h3>
<p>首先,我们重新组织了资源的加载顺序:</p>
<div class="jb51code"><pre class="brush:xhtml;">&lt;head&gt;
&lt;!-- 关键 CSS 内联 --&gt;
&lt;style&gt;
    /* 首屏关键样式 */
    .header, .hero { /* ... */ }
&lt;/style&gt;

&lt;!-- 非关键 CSS 异步加载 --&gt;
&lt;link rel="preload" href="/styles/main.css" rel="external nofollow"rel="external nofollow"as="style" onload="this.rel='stylesheet'"&gt;

&lt;!-- 延迟加载非关键 JavaScript --&gt;
&lt;script defer src="/js/main.js"&gt;&lt;/script&gt;
&lt;script async src="/js/analytics.js"&gt;&lt;/script&gt;
&lt;/head&gt;
</pre></div>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>2. 图片优化</h3>
<p>我们实现了一个渐进式图片加载策略:</p>
<div class="jb51code"><pre class="brush:js;">// components/ProgressiveImage.tsx
import { useState, useEffect } from 'react'

export function ProgressiveImage({ src, alt, width, height }: ImageProps) {
const = useState(getLowQualityUrl(src))

useEffect(() =&gt; {
    const img = new Image()
    img.src = src
    img.onload = () =&gt; {
      setCurrentSrc(src)
    }
}, )

return (
    &lt;img
      src={currentSrc}
      alt={alt}
      width={width}
      height={height}
      loading="lazy"
      decoding="async"
      className="transition-opacity duration-300"
    /&gt;
)
}
</pre></div>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>3. 代码分割和懒加载</h3>
<p>使用 webpack 和 React.lazy 实现智能代码分割:</p>
<div class="jb51code"><pre class="brush:js;">// 路由级别的代码分割
const ProductList = React.lazy(() =&gt; import('./pages/ProductList'))
const ProductDetail = React.lazy(() =&gt; import('./pages/ProductDetail'))

function App() {
return (
    &lt;Suspense fallback={&lt;Loading /&gt;}&gt;
      &lt;Routes&gt;
      &lt;Route path="/products" element={&lt;ProductList /&gt;} /&gt;
      &lt;Route path="/products/:id" element={&lt;ProductDetail /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/Suspense&gt;
)
}
</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>渲染优化</h2>
<p class="maodian"><a name="_lab2_2_3"></a></p><h3>1. 虚拟列表实现</h3>
<p>对于长列表,我们实现了虚拟滚动:</p>
<div class="jb51code"><pre class="brush:js;">// components/VirtualList.tsx
function VirtualList({ items, rowHeight, visibleRows }: VirtualListProps) {
const = useState(0)

const startIndex = Math.floor(scrollTop / rowHeight)
const visibleItems = items.slice(startIndex, startIndex + visibleRows)

return (
    &lt;div
      style={{ height: items.length * rowHeight }}
      onScroll={e =&gt; setScrollTop(e.currentTarget.scrollTop)}
    &gt;
      &lt;div style={{ transform: `translateY(${startIndex * rowHeight}px)` }}&gt;
      {visibleItems.map(item =&gt; (
          &lt;div key={item.id} style={{ height: rowHeight }}&gt;
            {item.content}
          &lt;/div&gt;
      ))}
      &lt;/div&gt;
    &lt;/div&gt;
)
}
</pre></div>
<p class="maodian"><a name="_lab2_2_4"></a></p><h3>2. 状态管理优化</h3>
<p>我们使用了细粒度的状态更新策略:</p>
<div class="jb51code"><pre class="brush:js;">// 优化前:整个组件树重渲染
const = useState({
list: [],
filters: {},
sorting: 'price'
})

// 优化后:分离关注点
const = useState([])
const = useState({})
const = useState('price')
</pre></div>
<p class="maodian"><a name="_lab2_2_5"></a></p><h3>3. 缓存策略</h3>
<p>实现了多层缓存机制:</p>
<div class="jb51code"><pre class="brush:js;">// utils/cache.ts
const cache = new Map()

export function withCache&lt;T&gt;(
key: string,
fetchFn: () =&gt; Promise&lt;T&gt;,
ttl: number = 3600000 // 1小时
): Promise&lt;T&gt; {
const cached = cache.get(key)
if (cached &amp;&amp; Date.now() - cached.timestamp &lt; ttl) {
    return Promise.resolve(cached.data)
}

return fetchFn().then(data =&gt; {
    cache.set(key, { data, timestamp: Date.now() })
    return data
})
}

// 使用示例
const getProductData = async (id: string) =&gt; {
return withCache(
    `product:${id}`,
    () =&gt; api.fetchProduct(id)
)
}
</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>性能监控</h2>
<p>为了持续监控性能,我们实现了性能指标收集:</p>
<div class="jb51code"><pre class="brush:js;">// utils/performance.ts
export function collectMetrics() {
const paint = performance.getEntriesByType('paint')
const navigation = performance.getEntriesByType('navigation')

return {
    FCP: paint.find(p =&gt; p.name === 'first-contentful-paint')?.startTime,
    TTFB: navigation.responseStart - navigation.requestStart,
    TTI: performance.now(), // 简化版 TTI
}
}

// 定期上报性能数据
setInterval(() =&gt; {
const metrics = collectMetrics()
analytics.send('performance', metrics)
}, 60000)
</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>优化成果</h2>
<p>经过一系列优化,我们取得了显著的成效:</p>
<ul><li>首屏加载时间:8s &rarr; 2s</li><li>首次内容绘制 (FCP):2.8s &rarr; 0.8s</li><li>最大内容绘制 (LCP):4.5s &rarr; 1.5s</li><li>页面交互延迟 (FID):300ms &rarr; 50ms</li></ul>
<p>最让我欣慰的是收到用户的反馈:&quot;网站变得超级快,用起来太舒服了!&quot;这让所有的优化工作都变得值得。😊</p>
<p class="maodian"><a name="_label5"></a></p><h2>写在最后</h2>
<p>前端性能优化是一个持续的过程,没有一劳永逸的解决方案。关键是要:</p>
<ul><li>建立性能指标基线</li><li>持续监控和优化</li><li>在开发阶段就注意性能问题</li><li>打造性能优化文化</li></ul>
<p></p>
頁: [1]
查看完整版本: 从加载到渲染的全链路提速的网站打开速度前端优化