JavaScript 延迟加载的方法

延迟加载(Lazy Loading)是一种优化网页性能的技术,它允许资源(如图片、脚本等)在需要时才被加载,而不是在页面初次加载时全部加载。这可以减少初始页面加载时间,提升用户体验,特别是在移动设备或网络条件不佳的情况下。以下是几种常见的 JavaScript 延迟加载方法:

1. 图片的延迟加载

使用 loading 属性

现代浏览器支持原生的图片延迟加载功能,通过为 <img> 标签添加 loading="lazy" 属性即可实现。

html 复制代码
<img src="image.jpg" alt="Description of image" loading="lazy">

这种方法简单易用,且不需要额外的 JavaScript 代码。

自定义 JavaScript 实现

如果需要更复杂的控制或者兼容性考虑,可以使用 JavaScript 来检测元素是否进入视口,并在适当的时候加载图片。

示例代码:
javascript 复制代码
function lazyLoadImages() {
  const images = document.querySelectorAll('img[data-src]');
  const config = {
    rootMargin: '0px',
    threshold: 0.1 // 当图片底部有10%进入视口时开始加载
  };

  let observer;

  function preloadImage(img) {
    const source = img.getAttribute('data-src');
    if (!source) return;
    img.src = source;
    img.removeAttribute('data-src');
  }

  function handleIntersect(entries, observer) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        preloadImage(entry.target);
        observer.unobserve(entry.target);
      }
    });
  }

  if ('IntersectionObserver' in window) {
    observer = new IntersectionObserver(handleIntersect, config);
    images.forEach(image => {
      observer.observe(image);
    });
  } else {
    // Fallback for browsers that do not support IntersectionObserver
    images.forEach(image => {
      preloadImage(image);
    });
  }
}

// Call the function on page load or when needed
document.addEventListener('DOMContentLoaded', lazyLoadImages);

2. 脚本的延迟加载

使用 asyncdefer 属性

对于外部 JavaScript 文件,可以通过设置 <script> 标签上的 asyncdefer 属性来实现异步加载。

  • async:尽可能早地下载脚本并在下载完成后立即执行,不会阻塞 DOM 解析。
  • defer :脚本会在 HTML 文档解析完成后但 DOMContentLoaded 事件触发前按顺序执行。
html 复制代码
<script src="script.js" async></script>
<script src="another-script.js" defer></script>
动态插入脚本

另一种方法是通过 JavaScript 动态创建并插入 <script> 元素,从而实现更灵活的控制。

示例代码:
javascript 复制代码
function loadScript(src, callback) {
  const script = document.createElement('script');
  script.src = src;
  script.onload = () => callback && callback();
  document.body.appendChild(script);
}

// Usage example
loadScript('https://example.com/script.js', () => {
  console.log('External script loaded and executed.');
});

3. 框架和库

利用现有的前端框架(如 Vue.js、React 等)提供的组件懒加载机制,可以在路由切换或其他特定条件下加载组件及其依赖项。此外,还有专门用于处理资源延迟加载的第三方库,例如 Lozad.jslazysizes

4. CSS 资源的延迟加载

虽然 CSS 通常不是延迟加载的重点,但在某些情况下也可以考虑延迟加载非关键路径上的样式表。一种方式是将这些样式表放在媒体查询中,只有当满足特定条件时才会加载。

html 复制代码
<link rel="stylesheet" href="print.css" media="print">
<link rel="stylesheet" href="mobile.css" media="(max-width: 768px)">

5. Intersection Observer API

虽然之前已经提到过 Intersection Observer API 可以用来实现图片的懒加载,但它的应用远不止于此。它也可以用于监听任何 DOM 元素何时进入或离开视口,并根据这个事件触发相应的操作。

示例:延迟加载视频
javascript 复制代码
const videos = document.querySelectorAll('video[data-src]');

if ('IntersectionObserver' in window) {
  const videoObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const video = entry.target;
        video.src = video.getAttribute('data-src');
        video.play();
        observer.unobserve(video);
      }
    });
  });

  videos.forEach(video => {
    videoObserver.observe(video);
  });
}
  • Prefetch :通过 <link rel="prefetch"> 提前下载用户可能需要的资源,例如下一个页面的 HTML、CSS 或 JavaScript 文件。这可以在后台悄悄进行,当用户导航到该页面时,资源已经准备好。

  • Preload :使用 <link rel="preload"> 强制浏览器优先加载指定资源,确保它们尽早可用。这对于关键路径上的资源特别有用,比如字体或重要的样式表。

html 复制代码
<!-- Prefetch -->
<link rel="prefetch" href="/next-page.html">

<!-- Preload -->
<link rel="preload" href="critical-font.woff2" as="font" type="font/woff2" crossorigin>

7. 动态导入(Dynamic Imports)

动态导入允许你按需加载模块,而不是在应用程序启动时全部加载。这对于大型单页应用(SPA)尤其重要,因为它可以帮助减少初始加载时间。

示例代码:
javascript 复制代码
// Traditional import
import('./module.js').then(module => {
  // Use the imported module
});

// Conditional dynamic import
if (condition) {
  import('./optional-module.js').then(optionalModule => {
    // Do something with optionalModule
  });
}

// In a function or event handler
function loadFeature() {
  return import('./feature.js');
}

document.getElementById('featureButton').addEventListener('click', () => {
  loadFeature().then(feature => {
    feature.init();
  });
});

8. Code Splitting

对于使用打包工具(如 Webpack)构建的应用程序,可以通过代码分割(Code Splitting)来将应用程序拆分为多个小块,只在需要时加载特定的部分。这样不仅可以减小初始加载量,还能提高缓存效率。

  • Webpack 动态导入 :结合 import() 语法和 Webpack 的魔法注释(magic comments),可以精确控制哪些部分应该被打包在一起以及如何异步加载。
javascript 复制代码
// Webpack magic comment for splitting chunks
const moduleA = 'module-a';
import(/* webpackChunkName: "chunk-name" */ `./${moduleA}`).then(({ default: mod }) => {
  console.log(mod);
});

9. 骨架屏(Skeleton Screens)

不是传统意义上的"延迟加载",但骨架屏是一种用户体验设计模式,它在实际内容加载之前显示一个简化版的界面布局。这种方法可以让用户感觉页面加载速度更快,即使实际上数据还在加载中。

html 复制代码
<div class="skeleton">
  <div class="skeleton__image"></div>
  <div class="skeleton__text"></div>
</div>

<script>
// Replace skeleton screen with actual content once loaded
fetch('/content')
  .then(response => response.json())
  .then(data => {
    document.querySelector('.skeleton').innerHTML = `
      <img src="${data.imageUrl}" alt="Image">
      <p>${data.text}</p>
    `;
  });
</script>

10. Service Workers

服务工作者(Service Workers)是运行在浏览器后台进程中的脚本,它们可以在没有网络连接的情况下提供离线访问功能。通过预缓存静态资源并在请求时从缓存中提供这些资源,可以有效减少对服务器的压力并加速页面加载。

注册 Service Worker 并缓存资源:
javascript 复制代码
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js').then(registration => {
      console.log('Service Worker registered with scope:', registration.scope);
    }).catch(error => {
      console.log('Service Worker registration failed:', error);
    });
  });
}
在 Service Worker 文件 (sw.js) 中定义缓存逻辑:
javascript 复制代码
const CACHE_NAME = 'my-site-cache-v1';
const urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/app.js'
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(urlsToCache))
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => response || fetch(event.request))
  );
});

总结

延迟加载不仅仅局限于图片和脚本,还可以应用于各种类型的资源和场景。利用现代浏览器提供的新特性(如 Intersection Observer、动态导入等),结合传统的优化手段(如 Prefetch 和 Preload),可以构建出更加高效且响应迅速的 Web 应用。此外,考虑到用户体验,采用骨架屏或 Service Workers 等技术也能为用户提供更好的浏览体验。

相关推荐
敢敢のwings14 分钟前
捯饬DeepScaleR-1.5B----最有可能在嵌入端部署的思考模型
服务器·开发语言
EPSDA2 小时前
Linux命名管道与共享内存
linux·运维·服务器·开发语言·c++
老K(郭云开)2 小时前
最新版Edge浏览器集成ActiveX控件之金山WpsDocFrame控件
前端·javascript·chrome·3d·中间件
一张假钞3 小时前
Python3操作MongoDB批量upsert
开发语言·数据库·python·mongodb
C#Thread4 小时前
C# 上位机--枚举
开发语言·c#
嘵奇4 小时前
Python练习11-20
开发语言·python
一路向前的月光4 小时前
React(6)
前端·javascript·react.js
游王子5 小时前
Python Pandas(9):Pandas 相关性分析
开发语言·python·pandas
不会玩技术的技术girl5 小时前
使用Java爬虫获取京东商品评论API接口(JD.item_review)数据
java·开发语言·爬虫
祁许5 小时前
【Vue】打包vue3+vite项目发布到github page的完整过程
前端·javascript·vue.js·github