JavaScript惰性加載的優(yōu)化技巧詳解
這是之前的一位朋友的酒桌之談,他之前負(fù)責(zé)的一個(gè)電商項(xiàng)目,剛剛開發(fā)萬,首頁加載時(shí)間特別長,體驗(yàn)很差,所以就開始排查,發(fā)現(xiàn)是在首頁一次性加載所有js導(dǎo)致的問題,這個(gè)問題在自己學(xué)習(xí)的時(shí)候并不明顯,往往被大家稱為編程習(xí)慣,甚至有的公司大佬進(jìn)行項(xiàng)目框架層級(jí)的硬性規(guī)定或者封裝,可以起到很好的作用,但是今天還是拿出來和大家分享一下:
場景描述
網(wǎng)站在首頁一次性加載了所有模塊的JavaScript資源,包括:
- 首屏輪播圖
- 商品推薦列表
- 用戶評論模塊
- 頁腳幫助中心
- 未可視區(qū)域的廣告和營銷組件
示例代碼
// 問題代碼示例 - 同步加載所有腳本
import Carousel from './components/Carousel'; // 首屏必要
import ProductList from './components/ProductList'; // 首屏必要
import Reviews from './components/Reviews'; // 需要滾動(dòng)到中部才顯示
import Ads from './components/Ads'; // 頁尾才顯示
import HelpCenter from './components/HelpCenter'; // 頁腳折疊區(qū)域
?
function HomePage() {
return (
<>
<Carousel />
<ProductList />
<Reviews />
<Ads />
<HelpCenter />
</>
);
}
問題
1.首屏加載時(shí)間過長
用戶需要等待所有JS下載并執(zhí)行完才能看到首屏內(nèi)容
Lighthouse評分中"First Contentful Paint"指標(biāo)很差
2.帶寬浪費(fèi)
加載了用戶可能永遠(yuǎn)不會(huì)看到的資源(如未滾動(dòng)的底部內(nèi)容)
3.主線程阻塞
大量JS同步執(zhí)行導(dǎo)致主線程長時(shí)間忙碌
用戶交互(如點(diǎn)擊搜索框)出現(xiàn)延遲
4.內(nèi)存占用過高
初始化了所有組件,包括那些不需要立即顯示的
排查思路
這個(gè)排查的思路很通用,大部份性能問題都可以從這里著手,如果是前端的新朋友,那么建議從開頭編寫完代碼之后,多依著下面的排查思路看看自己的代碼執(zhí)行步驟,然后思考,會(huì)有意想不到的收獲。
1.初步性能評估
打開Chrome DevTools (F12)
- 切換到 Network 面板
- 勾選 Disable cache (模擬首次訪問)
- 選擇 Fast 3G 網(wǎng)絡(luò)限速(放大問題)
錄制加載過程
- 刷新頁面開始錄制
- 觀察所有資源的加載順序和時(shí)間線
2.網(wǎng)絡(luò)面板分析
JS加載問題識(shí)別
按類型篩選 JS 資源
檢查:
- 是否首屏不需要的JS過早加載
- 是否有大體積JS阻塞渲染(紅色長條)
- JS文件是否并行加載
關(guān)鍵指標(biāo)查看
- Waterfall流中查找:
* 藍(lán)色豎線(DOMContentLoaded)
* 紅色豎線(Load)
- 關(guān)注:
* 首屏渲染完成時(shí)間
* 主線程被JS執(zhí)行阻塞的時(shí)間段
3.性能面板深度分析
Performance面板錄制
- 點(diǎn)擊 Record 后刷新頁面
- 停止后查看時(shí)間線
關(guān)鍵區(qū)域檢查
Main 線程活動(dòng):
- 長任務(wù)(超過50ms的黃色塊)
- JS編譯與執(zhí)行(紫色部分)
Network 網(wǎng)絡(luò)請求:
JS加載與執(zhí)行的關(guān)聯(lián)關(guān)系
Timings 標(biāo)記:
FP/FCP/FMP/LCP等關(guān)鍵時(shí)間點(diǎn)
典型問題模式識(shí)別
// 問題特征示例時(shí)間線:
[JS下載1][JS執(zhí)行1][JS下載2][JS執(zhí)行2]...
// 優(yōu)化后應(yīng)有:
[首屏JS下載][首屏渲染][惰性加載其他資源]
4.內(nèi)存面板檢查
Memory面板快照
取 Heap snapshot 比較:
- 頁面剛加載時(shí)
- 滾動(dòng)到底部后
內(nèi)存問題識(shí)別
- 檢查是否過早初始化了不必要組件
- 查看保留的DOM節(jié)點(diǎn)數(shù)量是否異常
5.Lighthouse自動(dòng)化審計(jì)
生成報(bào)告
- 切換到 Lighthouse 面板
- 勾選 Performance 選項(xiàng)
- 點(diǎn)擊 Generate report
關(guān)鍵指標(biāo)關(guān)注
- Opportunities中的建議:
* "Defer offscreen images"
* "Reduce unused JavaScript"
- Diagnostics中的:
* "Avoid enormous network payloads"
* "JavaScript execution time"
6.具體問題定位步驟
確定關(guān)鍵渲染路徑
在 Performance 錄制中:
- 找到 FCP (First Contentful Paint)
- 分析之前的所有JS活動(dòng)
識(shí)別非必要JS
// 在Console執(zhí)行:
performance.getEntriesByType('resource')
.filter(r => r.initiatorType === 'script')
.sort((a,b) => a.startTime - b.startTime)
.map(r => ({
name: r.name.split('/').pop(),
start: r.startTime,
duration: r.duration
}))
加載順序可視化
使用 Network 的時(shí)序圖:
- 拖動(dòng)選擇首屏?xí)r間段(0-FCP)
- 右鍵 → "Save as HAR with content"
嘗試解決
懶加載最直接的理解可以是按序加載,并不是一個(gè)很完整的操作,而是很多的細(xì)節(jié)拼湊或者是自己平常注意一種習(xí)慣吧。下面列出我自己習(xí)慣性的思路:
1. 動(dòng)態(tài)導(dǎo)入 (Dynamic Import)
import { lazy, Suspense } from 'react';
?
const Reviews = lazy(() => import('./components/Reviews'));
const Ads = lazy(() => import('./components/Ads'));
const HelpCenter = lazy(() => import('./components/HelpCenter'));
?
function HomePage() {
return (
<>
<Carousel />
<ProductList />
<Suspense fallback={<Spinner />}>
{/* 當(dāng)元素進(jìn)入視口時(shí)加載 */}
<LazyLoadComponent>
<Reviews />
</LazyLoadComponent>
<LazyLoadComponent>
<Ads />
</LazyLoadComponent>
<LazyLoadComponent>
<HelpCenter />
</LazyLoadComponent>
</Suspense>
</>
);
}
2. Intersection Observer實(shí)現(xiàn)視口觸發(fā)
// 自定義懶加載組件
const LazyLoadComponent = ({ children }) => {
const ref = useRef();
const [isVisible, setIsVisible] = useState(false);
?
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect();
}
},
{ threshold: 0.1 }
);
observer.observe(ref.current);
return () => observer.disconnect();
}, []);
?
return <div ref={ref}>{isVisible && children}</div>;
};
3. 圖片/iframe懶加載
<!-- 使用原生loading屬性 --> <img src="product.jpg" loading="lazy" alt="Product"> ? <!-- 或使用Intersection Observer實(shí)現(xiàn) --> <div class="lazy-image" data-src="product.jpg"></div>
性能影響數(shù)據(jù)(模擬)
| 指標(biāo) | 非惰性加載 | 惰性加載優(yōu)化后 |
|---|---|---|
| 首屏加載時(shí)間 | 4.2s | 1.8s |
| 總JS體積 | 1.8MB | 650KB(首屏) |
| Lighthouse性能評分 | 48 | 82 |
| 首次輸入延遲(FID) | 320ms | 110ms |
這些細(xì)節(jié)往往很小,但是很多,歡迎各位小伙伴一起討論。
到此這篇關(guān)于JavaScript惰性加載的優(yōu)化技巧詳解的文章就介紹到這了,更多相關(guān)JavaScript惰性加載優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS字符串和數(shù)組如何實(shí)現(xiàn)相互轉(zhuǎn)化
這篇文章主要介紹了JS字符串和數(shù)組如何實(shí)現(xiàn)相互轉(zhuǎn)化,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
javascript實(shí)現(xiàn)平滑無縫滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)平滑無縫滾動(dòng)的具體代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05
underscore之Collections_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
underscore為集合類對象提供了一致的接口。集合類是指Array和Object,暫不支持Map和Set。下面通過本文給大家分享underscore之Collections的相關(guān)知識(shí),需要的的朋友參考下吧2017-07-07
非常不錯(cuò)的功能強(qiáng)大代碼簡單的管理菜單美化版
由于網(wǎng)盤不穩(wěn)定,很多時(shí)候文件提示找不到,幸好U盤里存了. 喜歡這3個(gè)風(fēng)格的朋友們別在PM我啦.....我把文件傳到我服務(wù)器上了..2008-07-07
JS簡單實(shí)現(xiàn)禁止訪問某個(gè)頁面的方法
這篇文章主要介紹了JS簡單實(shí)現(xiàn)禁止訪問某個(gè)頁面的方法,涉及基本的頁面跳轉(zhuǎn)操作技巧,需要的朋友可以參考下2016-09-09
JavaScript 獲取任一float型小數(shù)點(diǎn)后兩位的小數(shù)
這篇文章主要介紹了JavaScript如何獲取小數(shù)任一小數(shù)點(diǎn)后的位數(shù)的小數(shù),需要的朋友可以參考下2014-06-06

