这是之前的一位朋友的酒桌之谈,他之前负责的一个电商项目,刚刚开发万,首页加载时间特别长,体验很差,所以就开始排查,发现是在首页一次性加载所有js导致的问题,这个问题在自己学习的时候并不明显,往往被大家称为编程习惯,甚至有的公司大佬进行项目框架层级的硬性规定或者封装,可以起到很好的作用,但是今天还是拿出来和大家分享一下:
场景描述
网站在首页一次性加载了所有模块的JavaScript资源,包括:
- 首屏轮播图
- 商品推荐列表
- 用户评论模块
- 页脚帮助中心
- 未可视区域的广告和营销组件
示例代码
// 问题代码示例 - 同步加载所有脚本 import Carousel from './components/Carousel'; // 首屏必要 import ProductList from './components/ProductList'; // 首屏必要 import Reviews from './components/Reviews'; // 需要滚动到中部才显示 import Ads from './components/Ads'; // 页尾才显示 import HelpCenter from './components/HelpCenter'; // 页脚折叠区域 function HomePage() { return ( <> <Carousel /> <ProductList /> <Reviews /> <Ads /> <HelpCenter /> </> ); }
问题
1.首屏加载时间过长
用户需要等待所有JS下载并执行完才能看到首屏内容
Lighthouse评分中”First Contentful Paint”指标很差
2.带宽浪费
加载了用户可能永远不会看到的资源(如未滚动的底部内容)
3.主线程阻塞
大量JS同步执行导致主线程长时间忙碌
用户交互(如点击搜索框)出现延迟
4.内存占用过高
初始化了所有组件,包括那些不需要立即显示的
排查思路
这个排查的思路很通用,大部份性能问题都可以从这里着手,如果是前端的新朋友,那么建议从开头编写完代码之后,多依着下面的排查思路看看自己的代码执行步骤,然后思考,会有意想不到的收获。
1.初步性能评估
打开Chrome DevTools (F12)
- 切换到 Network 面板
- 勾选 Disable cache (模拟首次访问)
- 选择 Fast 3G 网络限速(放大问题)
录制加载过程
- 刷新页面开始录制
- 观察所有资源的加载顺序和时间线
2.网络面板分析
JS加载问题识别
按类型筛选 JS 资源
检查:
- 是否首屏不需要的JS过早加载
- 是否有大体积JS阻塞渲染(红色长条)
- JS文件是否并行加载
关键指标查看
– Waterfall流中查找:
* 蓝色竖线(DOMContentLoaded)
* 红色竖线(Load)
– 关注:
* 首屏渲染完成时间
* 主线程被JS执行阻塞的时间段
3.性能面板深度分析
Performance面板录制
- 点击 Record 后刷新页面
- 停止后查看时间线
关键区域检查
Main 线程活动:
- 长任务(超过50ms的黄色块)
- JS编译与执行(紫色部分)
Network 网络请求:
JS加载与执行的关联关系
Timings 标记:
FP/FCP/FMP/LCP等关键时间点
典型问题模式识别
// 问题特征示例时间线:
[JS下载1][JS执行1][JS下载2][JS执行2]…
// 优化后应有:
[首屏JS下载][首屏渲染][惰性加载其他资源]
4.内存面板检查
Memory面板快照
取 Heap snapshot 比较:
- 页面刚加载时
- 滚动到底部后
内存问题识别
- 检查是否过早初始化了不必要组件
- 查看保留的DOM节点数量是否异常
5.Lighthouse自动化审计
生成报告
- 切换到 Lighthouse 面板
- 勾选 Performance 选项
- 点击 Generate report
关键指标关注
– Opportunities中的建议:
* “Defer offscreen images”
* “Reduce unused JavaScript”
– Diagnostics中的:
* “Avoid enormous network payloads”
* “JavaScript execution time”
6.具体问题定位步骤
确定关键渲染路径
在 Performance 录制中:
- 找到 FCP (First Contentful Paint)
- 分析之前的所有JS活动
识别非必要JS
// 在Console执行: 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 的时序图:
- 拖动选择首屏时间段(0-FCP)
- 右键 → “Save as HAR with content”
尝试解决
懒加载最直接的理解可以是按序加载,并不是一个很完整的操作,而是很多的细节拼凑或者是自己平常注意一种习惯吧。下面列出我自己习惯性的思路:
1. 动态导入 (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 />}> {/* 当元素进入视口时加载 */} <LazyLoadComponent> <Reviews /> </LazyLoadComponent> <LazyLoadComponent> <Ads /> </LazyLoadComponent> <LazyLoadComponent> <HelpCenter /> </LazyLoadComponent> </Suspense> </> ); }
2. Intersection Observer实现视口触发
// 自定义懒加载组件 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实现 --> <div class="lazy-image" data-src="product.jpg"></div>
性能影响数据(模拟)
指标 | 非惰性加载 | 惰性加载优化后 |
---|---|---|
首屏加载时间 | 4.2s | 1.8s |
总JS体积 | 1.8MB | 650KB(首屏) |
Lighthouse性能评分 | 48 | 82 |
首次输入延迟(FID) | 320ms | 110ms |
这些细节往往很小,但是很多,欢迎各位小伙伴一起讨论。
到此这篇关于JavaScript惰性加载的优化技巧详解的文章就介绍到这了,更多相关JavaScript惰性加载优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
来源链接:https://www.jb51.net/javascript/339287pke.htm
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容