当前位置: 首页 > news >正文

性能优化之图片懒加载

本文已参与「新人创作礼」活动,一起开启掘金创作之路

为什么要懒加载

图片资源体积影响加载请求,影响性能很严重
花最少的时间,最大的优化

1. 使用浏览器级别懒加载

兼容性还行,还简单
chrome 76以后出的
可以实现懒加载效果

 <img src="" lazy="loading" alt="">

背景图懒加载

图片懒加载的关键在于获取元素的位置,
并判断其是否出现在视口。故有以下三种方式

滚动监听+scrollTop+offsetTop+innerHeight
滚动监听+getBoundingClientRect()
intersectionObserve()

IntersectionObserver

ntersection Observer API
可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。
意思就是浏览器可以观察元素是否在可视区内。

/*
  初始化 Intersectionobserver 类
  `callback`是当元素的可见性变化时候的回调函数,`options`是一些配置项
*/
const observer = new IntersectionObserver(callback, options);
/* 监听对应的 DOM 元素 */
observer.observe(DOM);
/* 取消对 DOM 元素的监听 */
observer.unobserve(DOM);
/*
  root:用于观察的根元素,默认是浏览器的视口
  rootMargin:用来缩放视窗的的大小,用法和 CSS 类似,
              `10px 10px 30px 20px`表示top、right、bottom 和 left 扩大的对应值,
              `10px 20px 5px`表示top,left&right, bottom 扩大的对应值
  thresholds: 当被监听的元素与根元素的交叉比例达到该值时触发回调函数,
              默认为`[0]`, `0.1`表示当交叉比例达到`0.1`时触发回调,
              `[0,0.5]`表示当交叉比例达到`0`和`0.5`时都会触发回调
*/
const options = {
  root: null,
  rootMargin: 0,
  thresholds: 1,
};
/*
  回调函数在监听的 DOM 元素可见性变化时触发
  entries 中有多个参数,我们使用到以下两个参数
  isIntersecting 表示观察的元素是否出现在 root 元素可视区,
  intersectionRatio 表示观察的元素出现在 root 元素的比例
*/
function callback(entries) {
  entries.forEach(entry => {
    if (entry?.isIntersecting || entry?.intersectionRatio > 0) {
      // 在此处进行图片地址替换
    }
  });
}

3. 使用 getBoundingClientRect

当你的网站非常注重浏览器的兼容性时,以上的方法就无法满足你的需求,

我们需要使用scroll及其他事件处理程序配合getBoundingClientRect来实现代码降级,

从而确定确定元素是否位于视区中。此方法几乎可以兼容所以浏览器,

但是性能不如intersectionObserve和loading=lazy
// 用于存当前页面已有的图片
observerMap = new Map();
// 通过滚动事件触发 scrollHandle 回调
window.addEventListener('scroll', scrollHandle, true);
// 每次回调里进行遍历已有图片列表
function scrollHandle() {
  for (let dom of observerlist) {
    // 判断元素是否在可视区从而判断是否替换真实地址
  }
}
// 判断元素是否在可视区
function checkDomInView(element) {
  const viewwidth = window.innerWidth || document.documentElement.clientWidth;
  const viewHeight = window.innerHeight || document.documentElement.clientHeight;
  const { top, right, bottom, left } = element.getBoundingClientRect();
  return left < viewwidth && right > 0 && top < viewHeight && bottom > 0;
}

react实现分装

    <script>
      //分装函数
      const defauleConfig = {
        value: "",
        inactiveValue: "",
        disabled: false,
      };
      const useInView = (targetRef, config) => {
        const {
          value = "",
          inactiveValue = "",
          disabled = false,
        } = {
          ...defauleConfig,
          ...config,
        };
        const [inView, setInView] = useState(
          typeof IntersectionObserver === "undefined",
        );
        const [currentValue, setCurrentVale] = useState(
          typeof IntersectionObserver === "undefined" ? value : inactiveValue,
        );

        useEffect(() => {
          const target = targetRef.current;
          if (!target || typeof IntersectionObserver === "undefined") {
            setInView(true);
            setCurrentVale(value);
            return () => {};
          }
          const observable = new IntersectionObserver((entries) => {
            const { isIntersecting, IntersectionRatio } = entries[0] || {};
            if (isIntersecting || IntersectionRatio > 0) {
              setCurrentVale(value);
              setInView(true);
              observable.disconnect();
            }
          });
          observable.observe(target);
          return () => {
            observable.disconnect();
          };
        }, [value]);
        return [inView, disabled ? value : currentValue];
      };

      //背景图懒加载组件
      const LazyImageContext = createContext({});
      const LazyBackground = memo(
        forwardRef((props, _ref) => {
          const { disabled, style, children, ...resetProps } = props;
          const { disabled: _disabled } = useContext(LazyImageContext);
          const ref = useRef(_ref);
          const inView = useInView(ref);
          const finalDisabled =
            typeof disabled === "undefined" ? _disabled : disabled;
          const _style =
            !finalDisabled && !inView ? { backgroundImage: "none" } : {};

          return;
          <div
            {...resetProps}
            ref={(el) => (ref.current = el)}
            style={{ ...style, ..._style }}
          >
            {children}
          </div>;
        }),
      );


//图片懒加载组件
      const LazyImage = memo(
        forwardRef((props, _ref) => {
          const {
            src,
            disabled,
            loading = "lazy",
            alt = "",
            style,
            ...restProps
          } = props;

          const { disabled: _disabled } = useContext(LazyImageContext);
          const ref = useRef(_ref);

          const finalDisabled =
            typeof disabled === "undefined" ? _disabled : disabled;
          const [, currentSrc] = useInView(ref, {
            value: src,
            disabled: finalDisabled,
          });
          const _style = !currentSrc ? { visibility: "hidden" } : {};

          return (
            <img
              {...restProps}
              loading={loading}
              style={{ ...style, ..._style }}
              src={currentSrc}
              ref={(el) => (ref.current = el)}
            />
          );
        }),
      );
    </script>

相关文章:

  • Linux多线程篇【5】——线程池
  • 指针成员操作符
  • python中应对各种机制
  • css实现时钟
  • “蔚来杯“2022牛客暑期多校训练营8 补题题解(F)
  • 【数据结构与算法】之深入解析“解出数学表达式的学生分数”的求解思路与算法示例
  • 给妈妈做个相册——在服务器上搭建Lychee相册的保姆级教程
  • 编程之路22
  • 适配器模式是个啥,在Spring中又用来干啥了?
  • 183. 从不订购的客户—not in()、左连接
  • LED灯实验
  • vue中ref的作用
  • JSP简介
  • 湖仓一体电商项目(八):业务实现之编写写入ODS层业务代码
  • 基于深度学习的多人步态识别系统(YOLOV5+DeepSort+GaitSet+Segmentation)
  • [译]前端离线指南(上)
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • ➹使用webpack配置多页面应用(MPA)
  • Angular6错误 Service: No provider for Renderer2
  • Babel配置的不完全指南
  • canvas 五子棋游戏
  • go append函数以及写入
  • IDEA常用插件整理
  • Java精华积累:初学者都应该搞懂的问题
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • SQLServer之创建数据库快照
  • Sublime Text 2/3 绑定Eclipse快捷键
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • Vue--数据传输
  • XML已死 ?
  • 初探 Vue 生命周期和钩子函数
  • 大数据与云计算学习:数据分析(二)
  • 批量截取pdf文件
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 深度学习中的信息论知识详解
  • 跳前端坑前,先看看这个!!
  • 我是如何设计 Upload 上传组件的
  • 一个SAP顾问在美国的这些年
  • 用 Swift 编写面向协议的视图
  • 阿里云移动端播放器高级功能介绍
  • 国内开源镜像站点
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #include到底该写在哪
  • (003)SlickEdit Unity的补全
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (31)对象的克隆
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (八)Spring源码解析:Spring MVC
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (二)Linux——Linux常用指令
  • (篇九)MySQL常用内置函数
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (转)GCC在C语言中内嵌汇编 asm __volatile__