JavaScript惰性加載的優(yōu)化技巧詳解
這是之前的一位朋友的酒桌之談,他之前負(fù)責(zé)的一個(gè)電商項(xiàng)目,剛剛開(kāi)發(fā)萬(wàn),首頁(yè)加載時(shí)間特別長(zhǎng),體驗(yàn)很差,所以就開(kāi)始排查,發(fā)現(xiàn)是在首頁(yè)一次性加載所有js導(dǎo)致的問(wèn)題,這個(gè)問(wèn)題在自己學(xué)習(xí)的時(shí)候并不明顯,往往被大家稱為編程習(xí)慣,甚至有的公司大佬進(jìn)行項(xiàng)目框架層級(jí)的硬性規(guī)定或者封裝,可以起到很好的作用,但是今天還是拿出來(lái)和大家分享一下:
場(chǎng)景描述
網(wǎng)站在首頁(yè)一次性加載了所有模塊的JavaScript資源,包括:
- 首屏輪播圖
- 商品推薦列表
- 用戶評(píng)論模塊
- 頁(yè)腳幫助中心
- 未可視區(qū)域的廣告和營(yíng)銷組件
示例代碼
// 問(wèn)題代碼示例 - 同步加載所有腳本 import Carousel from './components/Carousel'; // 首屏必要 import ProductList from './components/ProductList'; // 首屏必要 import Reviews from './components/Reviews'; // 需要滾動(dòng)到中部才顯示 import Ads from './components/Ads'; // 頁(yè)尾才顯示 import HelpCenter from './components/HelpCenter'; // 頁(yè)腳折疊區(qū)域 ? function HomePage() { return ( <> <Carousel /> <ProductList /> <Reviews /> <Ads /> <HelpCenter /> </> ); }
問(wèn)題
1.首屏加載時(shí)間過(guò)長(zhǎng)
用戶需要等待所有JS下載并執(zhí)行完才能看到首屏內(nèi)容
Lighthouse評(píng)分中"First Contentful Paint"指標(biāo)很差
2.帶寬浪費(fèi)
加載了用戶可能永遠(yuǎn)不會(huì)看到的資源(如未滾動(dòng)的底部?jī)?nèi)容)
3.主線程阻塞
大量JS同步執(zhí)行導(dǎo)致主線程長(zhǎng)時(shí)間忙碌
用戶交互(如點(diǎn)擊搜索框)出現(xiàn)延遲
4.內(nèi)存占用過(guò)高
初始化了所有組件,包括那些不需要立即顯示的
排查思路
這個(gè)排查的思路很通用,大部份性能問(wèn)題都可以從這里著手,如果是前端的新朋友,那么建議從開(kāi)頭編寫完代碼之后,多依著下面的排查思路看看自己的代碼執(zhí)行步驟,然后思考,會(huì)有意想不到的收獲。
1.初步性能評(píng)估
打開(kāi)Chrome DevTools (F12)
- 切換到 Network 面板
- 勾選 Disable cache (模擬首次訪問(wèn))
- 選擇 Fast 3G 網(wǎng)絡(luò)限速(放大問(wèn)題)
錄制加載過(guò)程
- 刷新頁(yè)面開(kāi)始錄制
- 觀察所有資源的加載順序和時(shí)間線
2.網(wǎng)絡(luò)面板分析
JS加載問(wèn)題識(shí)別
按類型篩選 JS 資源
檢查:
- 是否首屏不需要的JS過(guò)早加載
- 是否有大體積JS阻塞渲染(紅色長(zhǎng)條)
- JS文件是否并行加載
關(guān)鍵指標(biāo)查看
- Waterfall流中查找:
* 藍(lán)色豎線(DOMContentLoaded)
* 紅色豎線(Load)
- 關(guān)注:
* 首屏渲染完成時(shí)間
* 主線程被JS執(zhí)行阻塞的時(shí)間段
3.性能面板深度分析
Performance面板錄制
- 點(diǎn)擊 Record 后刷新頁(yè)面
- 停止后查看時(shí)間線
關(guān)鍵區(qū)域檢查
Main 線程活動(dòng):
- 長(zhǎng)任務(wù)(超過(guò)50ms的黃色塊)
- JS編譯與執(zhí)行(紫色部分)
Network 網(wǎng)絡(luò)請(qǐng)求:
JS加載與執(zhí)行的關(guān)聯(lián)關(guān)系
Timings 標(biāo)記:
FP/FCP/FMP/LCP等關(guān)鍵時(shí)間點(diǎn)
典型問(wèn)題模式識(shí)別
// 問(wèn)題特征示例時(shí)間線:
[JS下載1][JS執(zhí)行1][JS下載2][JS執(zhí)行2]...
// 優(yōu)化后應(yīng)有:
[首屏JS下載][首屏渲染][惰性加載其他資源]
4.內(nèi)存面板檢查
Memory面板快照
取 Heap snapshot 比較:
- 頁(yè)面剛加載時(shí)
- 滾動(dòng)到底部后
內(nèi)存問(wèn)題識(shí)別
- 檢查是否過(guò)早初始化了不必要組件
- 查看保留的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.具體問(wèn)題定位步驟
確定關(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性能評(píng)分 | 48 | 82 |
首次輸入延遲(FID) | 320ms | 110ms |
這些細(xì)節(jié)往往很小,但是很多,歡迎各位小伙伴一起討論。
到此這篇關(guān)于JavaScript惰性加載的優(yōu)化技巧詳解的文章就介紹到這了,更多相關(guān)JavaScript惰性加載優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS字符串和數(shù)組如何實(shí)現(xiàn)相互轉(zhuǎn)化
這篇文章主要介紹了JS字符串和數(shù)組如何實(shí)現(xiàn)相互轉(zhuǎn)化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07微信小程序仿知乎實(shí)現(xiàn)評(píng)論留言功能
這篇文章主要為大家詳細(xì)介紹了微信小程序仿知乎實(shí)現(xiàn)評(píng)論留言功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11javascript實(shí)現(xiàn)平滑無(wú)縫滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)平滑無(wú)縫滾動(dòng)的具體代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05underscore之Collections_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
underscore為集合類對(duì)象提供了一致的接口。集合類是指Array和Object,暫不支持Map和Set。下面通過(guò)本文給大家分享underscore之Collections的相關(guān)知識(shí),需要的的朋友參考下吧2017-07-07非常不錯(cuò)的功能強(qiáng)大代碼簡(jiǎn)單的管理菜單美化版
由于網(wǎng)盤不穩(wěn)定,很多時(shí)候文件提示找不到,幸好U盤里存了. 喜歡這3個(gè)風(fēng)格的朋友們別在PM我啦.....我把文件傳到我服務(wù)器上了..2008-07-07JS簡(jiǎn)單實(shí)現(xiàn)禁止訪問(wèn)某個(gè)頁(yè)面的方法
這篇文章主要介紹了JS簡(jiǎn)單實(shí)現(xiàn)禁止訪問(wèn)某個(gè)頁(yè)面的方法,涉及基本的頁(yè)面跳轉(zhuǎn)操作技巧,需要的朋友可以參考下2016-09-09JavaScript 獲取任一float型小數(shù)點(diǎn)后兩位的小數(shù)
這篇文章主要介紹了JavaScript如何獲取小數(shù)任一小數(shù)點(diǎn)后的位數(shù)的小數(shù),需要的朋友可以參考下2014-06-06JS實(shí)現(xiàn)canvas簡(jiǎn)單小畫板功能
這篇文章主要為大家詳細(xì)介紹了JS實(shí)現(xiàn)canvas簡(jiǎn)單小畫板功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06