JavaScript惰性加载优化实例

#JavaScript性能优化实战#

这是之前的一位朋友的酒桌之谈,他之前负责的一个电商项目,刚刚开发万,首页加载时间特别长,体验很差,所以就开始排查,发现是在首页一次性加载所有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. 录制加载过程

    • 刷新页面开始录制

    • 观察所有资源的加载顺序和时间线

二、网络面板分析

  1. JS加载问题识别

    • 按类型筛选 JS 资源

    • 检查:

      • 是否首屏不需要的JS过早加载

      • 是否有大体积JS阻塞渲染(红色长条)

      • JS文件是否并行加载

  2. 关键指标查看

    markdown

    复制

    - Waterfall流中查找:
      * 蓝色竖线(DOMContentLoaded)
      * 红色竖线(Load)
    - 关注:
      * 首屏渲染完成时间
      * 主线程被JS执行阻塞的时间段

三、性能面板深度分析

  1. Performance面板录制

    • 点击 Record 后刷新页面

    • 停止后查看时间线

  2. 关键区域检查

    • Main 线程活动:

      • 长任务(超过50ms的黄色块)

      • JS编译与执行(紫色部分)

    • Network 网络请求:

      • JS加载与执行的关联关系

    • Timings 标记:

      • FP/FCP/FMP/LCP等关键时间点

  3. 典型问题模式识别

    javascript

    复制

    // 问题特征示例时间线:
    [JS下载1][JS执行1][JS下载2][JS执行2]...
    // 优化后应有:
    [首屏JS下载][首屏渲染][惰性加载其他资源]

四、内存面板检查

  1. Memory面板快照

    • Heap snapshot 比较:

      • 页面刚加载时

      • 滚动到底部后

  2. 内存问题识别

    • 检查是否过早初始化了不必要组件

    • 查看保留的DOM节点数量是否异常

五、Lighthouse自动化审计

  1. 生成报告

    • 切换到 Lighthouse 面板

    • 勾选 Performance 选项

    • 点击 Generate report

  2. 关键指标关注

    markdown

    复制

    - Opportunities中的建议:
      * "Defer offscreen images"
      * "Reduce unused JavaScript"
    - Diagnostics中的:
      * "Avoid enormous network payloads"
      * "JavaScript execution time"

六、具体问题定位步骤

  1. 确定关键渲染路径

    • Performance 录制中:

      • 找到 FCP (First Contentful Paint)

      • 分析之前的所有JS活动

  2. 识别非必要JS

    javascript

    复制

    // 在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
      }))
  3. 加载顺序可视化

    • 使用 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.2s1.8s
总JS体积1.8MB650KB(首屏)
Lighthouse性能评分4882
首次输入延迟(FID)320ms110ms

这些细节往往很小,但是很多,欢迎各位小伙伴一起讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大泽九章

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值