面试官:首页大图如何优化?

1. 图片压缩

图像压缩的基本原理是通过减少图像中每个像素的信息和颜色深度来减小文件大小。这可以通过改变图像的分辨率、压缩算法和色彩空间来实现。例如,JPEG 格式使用离散余弦变换 (DCT) 和量化来压缩图像,并且可以设置不同的压缩质量级别,以平衡文件大小和图像质量。

以下是常见的图片压缩工具:

  1. TinyPNG:TinyPNG 是一个在线工具,可自动优化 PNG 和 JPEG 图像,并使其在保持高质量的同时减小文件大小。

  2. Kraken.io:Kraken.io 是另一个在线工具,可将 JPEG、PNG 和 WebP 图像压缩到最小,并保持高质量的图片输出。

  3. ImageOptim:ImageOptim 是一个本地应用程序,可批量压缩 PNG、JPEG 和 GIF 图像,并删除元数据以减小文件大小。

  4. Photoshop:Photoshop 是一款功能强大的图像编辑软件,可使用“保存为 Web”选项将图像优化为最佳格式和质量。

注意事项:

  1. 不要过度压缩:过度压缩会导致图像失真,影响图像质量。应根据实际需要选择适当的压缩级别。

  2. 保持纵横比:对于长宽比例不同的图像,应该尽可能保留原始比例,以避免图像变形或扭曲。

  3. 选择正确的图像格式:不同的图像格式适用于不同类型的图像。JPEG 格式适合照片和渐变色图像,PNG 格式适合图标、文本和透明图像,GIF 格式适合动画图像。

  4. 清除元数据:某些图像格式包含元数据(例如 EXIF 数据),这些数据可以泄漏敏感信息并增加文件大小。应清除不必要的元数据以减小文件大小。

  5. 注意版权问题:压缩图像时应遵循版权法规定,并确保有权使用和分享压缩后的图像。

以下是一个示例代码,演示如何使用 Squoosh 进行图像压缩:

const blob = await fetch('image.jpg').then(r => r.blob());
const squooshResult = await self.squoosh.encode(blob, {
  mozjpeg: {
    quality75,
  },
  webp: {
    quality75,
  },
});
console.log(`Compressed image size: ${squooshResult.byteLength} bytes`);

2. 图片分割

图片分割是一种将超大图片分成小块并按需加载的技术。这可以显着提高页面加载速度和响应性,并降低带宽使用。

将超大图片分成小块可以通过使用 canvas API 和 context.drawImage() 方法来实现。该方法将图片分成网格,并使用 drawImage() 方法在每个网格中呈现一个小图像。

以下是一个示例代码,演示如何使用 canvas 分割图像:

const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');

// Load the image to be split into tiles.
const img = new Image();
img.src = 'image.jpg';
await img.decode();

// Set the tile size.
const tileSize = 1024;

// Split the image into tiles and draw each tile onto the canvas.
for (let y = 0; y < img.height; y += tileSize) {
  for (let x = 0; x < img.width; x += tileSize) {
    const width = Math.min(tileSize, img.width - x);
    const height = Math.min(tileSize, img.height - y);
    context.drawImage(img, x, y, width, height, x, y, width, height);
  }
}

// Convert the canvas to a blob and create an object URL for it.
const blob = await new Promise(resolve => canvas.toBlob(resolve));
const objectUrl = URL.createObjectURL(blob);

// Create a new image element and set its src attribute to the object URL.
const tileImage = new Image();
tileImage.src = objectUrl;

// Add the image element to the DOM.
document.body.appendChild(tileImage);

按需加载图像可以通过监听用户滚动事件来实现。当用户滚动到需要加载的部分时,使用 Image 对象动态加载并添加相应的小块图像。

以下是一个示例代码,演示如何实现按需加载图像:

const tileWidth = 1024;
const tileHeight = 1024;
const totalWidth = 8192;
const totalHeight = 8192;

// Load the initial tiles.
for (let x = 0; x < totalWidth; x += tileWidth) {
  for (let y = 0; y < totalHeight; y += tileHeight) {
    loadTile(x, y);
  }
}

// Listen for scroll events and load additional tiles as needed.
window.addEventListener('scroll', () => {
  const viewportWidth = window.innerWidth;
  const viewportHeight = window.innerHeight;
  const scrollTop = window.pageYOffset;
  const scrollLeft = window.pageXOffset;

  for (let x = 0; x < totalWidth; x += tileWidth) {
    for (let y = 0; y < totalHeight; y += tileHeight) {
      const tileRight = x + tileWidth;
      const tileBottom = y + tileHeight;

      if (
        tileRight > scrollLeft &&
        x < scrollLeft + viewportWidth &&
        tileBottom > scrollTop &&
        y < scrollTop + viewportHeight
      ) {
        loadTile(x, y);
      }
    }
  }
});

function loadTile(x, y{
  const img = new Image();
  img.src = `tile_${x}_${y}.jpg`;
  img.onload = () => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.drawImage(img, 00, tileWidth, tileHeight);
    const blob = canvas.toBlob();
    const objectUrl = URL.createObjectURL(blob);

    const tileImage = new Image();
    tileImage.src = objectUrl;
    tileImage.style.position = 'absolute';
    tileImage.style.left = `${x}px`;
    tileImage.style.top = `${y}px`;

    document.body.appendChild(tileImage);
  };
}

因为只有需要加载的部分被加载,而不是整个超大图像,从而显著提高页面加载速度和响应性,并降低带宽使用,同时还可以提高页面交互性和响应性。

缺点: 需要更多的代码来实现,并且可能需要对图像进行复杂的处理,尤其是在分割和重组过程中。另外,该方法可能会导致更多的 HTTP 请求,从而增加服务器负担。此外,由于图片分割涉及使用 JavaScript 对图像进行处理,因此它可能会降低某些设备的性能,并且可能会阻止较慢的设备访问网站。

3. 延迟加载

延迟加载(lazy loading)是一种优化网站性能的技术,它允许在需要时加载图像、视频和其他资源,而不是在页面加载时立即加载这些资源。

延迟加载的基本原理是将图像或其他资源的加载推迟到用户滚动到相应位置时再进行。这可以通过使用 JavaScript 进行事件侦听器和 DOM 操作来实现。

当用户滚动到一个包含延迟加载资源的区域时,会触发 JavaScript 事件处理程序,该处理程序检查资源是否位于视口内,如果是,则相应的资源被加载。这种方法可以显著减少页面首次加载时间,并提高用户体验。

实现延迟加载:

  1. 使用第三方库:可以使用第三方库,如 Lazy Load、Lozad.js 等,它们提供了简单易用的 API,可以在需要时加载图像和其他资源。

  2. 手动实现:也可以手动实现延迟加载,例如,使用 Intersection Observer API 来检测图像是否位于视口内,并在需要时加载它。以下是一个基本的示例代码:

const images = document.querySelectorAll('img[data-src]');

const options = {
  rootMargin'0px',
  threshold0.5
};

const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.intersectionRatio > 0) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, options);

images.forEach(img => {
  observer.observe(img);
});

注意事项:

  1. 不要过度使用:延迟加载应该只应用于大型图像和视频等特定类型的资源,而不应该应用于所有资源。

  2. 提供默认占位符:为了避免页面元素在加载期间出现的跳跃或闪烁,应该提供默认占位符,并在资源加载完毕后替换它们。

  3. 考虑 SEO:由于搜索引擎的爬虫不会执行 JavaScript,因此如果使用延迟加载技术,请确保在标记中提供有关图像和其他资源的详细说明。

4. WebP 格式

WebP 是一种现代的图像格式,由 Google 开发,旨在提供更高质量的压缩和更小的文件大小。WebP 的基本原理是使用预测算法和无损压缩来减小文件大小。它支持无损压缩、有损压缩和透明度,并可在色彩空间中使用 sRGB 和 YUV。

与其他图像格式相比,WebP 有以下优势

  1. 更小的文件大小:WebP 比 JPEG 和 PNG 格式具有更好的压缩率,可以将文件大小减少多达 34%。

  2. 更快的加载速度:由于文件大小较小,WebP 图像可更快地加载,从而提高网站性能。

  3. 更好的图像质量:WebP 可以提供更高质量的压缩,同时保持更少的失真和伪影。

  4. 更广泛的浏览器支持:WebP 支持 Chrome、Firefox、Edge 等现代浏览器,并且可以使用 polyfill 在不支持 WebP 的浏览器上进行回退。

要使用 WebP,需要将源图像转换为 WebP 格式,然后在网站上使用新的 WebP 图像。以下是一些常见的方法:

1,使用在线转换工具

可使用在线工具,如 Convertio、Squoosh 等将图像转换为 WebP 格式。这些工具可以轻松地将 JPEG、PNG 和其他格式的图像转换为 WebP。

2,使用命令行工具

可使用 Google 提供的命令行工具 cwebp 来将图像转换为 WebP 格式。例如,以下命令将 PNG 图像转换为 WebP 格式:

$ cwebp image.png -o image.webp

3,使用 JavaScript 库

可使用 JavaScript 库,如 WebPJS、Modernizr 等来检查浏览器是否支持 WebP,并在需要时提供 WebP 图像的回退选项。

WebP 的兼容性问题

WebP 在现代浏览器中得到了广泛支持,但在一些较旧的浏览器和设备上可能不受支持。在这种情况下,应提供回退选项以确保图像始终可见。

为了检测浏览器是否支持 WebP,可以使用以下代码:

function supportsWebP({
  return (typeof createImageBitmap === 'function') && 
         (window.chrome || window.opera) &&
         (/Chrome\/([0-9]+)/.exec(navigator.userAgent) ||
          /CriOS\/([0-9]+)/.exec(navigator.userAgent)) &&
         (/Edge\/([0-9]+)/.exec(navigator.userAgent) ||
          /Edg\/([0-9]+)/.exec(navigator.userAgent));
}

if (supportsWebP()) {
  console.log('WebP is supported!');
else {
  console.log('WebP is not supported.');
}

5. CDN 缓存

CDN(Content Delivery Network)是一种通过在全球范围内部署服务器和数据中心来提高网站性能和可用性的技术。

CDN 缓存是将网站内容(如图像、视频、CSS、JavaScript 等)复制到 CDN 上,并将其缓存到离终端用户最近的边缘节点上。当用户请求这些资源时,它们将从 CDN 边缘节点而不是源服务器中获取。

常见的 CDN 缓存实现方式:

  1. 全站加速:通过将整个网站托管到 CDN 上,可以将所有内容缓存到边缘节点上,从而提高全站的性能和可用性。

  2. 部分加速:针对部分静态资源(如图像、视频、CSS、JavaScript 等),可以将其缓存到 CDN 边缘节点上,并让用户从最近的节点获取它们。

  3. 动态加速:对于动态内容(如 HTML 页面、API 调用等),可以使用 CDN 缓存来减少源服务器负载和网络延迟。这可以通过使用 Cache-Control 和 Expires 标头来配置 CDN 来实现。

注意事项:

  1. 缓存更新:由于 CDN 缓存是静态的,因此如果源服务器上的内容发生更改,则需要更新缓存以确保用户看到最新版本的内容。

  2. 静态资源优化:仅将静态资源缓存到 CDN 上是不够的,还应使用优化技术,如 Gzip 压缩、图像压缩等,以进一步加快页面加载速度。

  3. 安全性问题:由于 CDN 缓存可能包含敏感信息或受版权保护的内容,因此必须采取安全措施来确保仅授权用户可以访问它们。

6. 图像预加载

图像预加载的基本原理是在页面加载之前或同时进行图像加载。这可以通过使用 JavaScript 来实现,在 DOMContentLoaded 事件触发之前将图像插入到文档中,并设置 CSS 属性 display:none 隐藏图像。然后,当需要使用这些图像时,它们已经完全加载,并可以立即显示。

图像预加载的优点

常见的方法来实现图像预加载:

  1. 使用 JavaScript:可以使用 JavaScript 来将图像的 URL 存储在数组中,并在页面加载时循环遍历数组以加载图像。
const images = ['image1.jpg''image2.jpg''image3.jpg'];

images.forEach(image => {
  const img = new Image();
  img.src = image;
});
  1. 使用 CSS:可以在 CSS 中将预加载的图像定义为背景图像,并使用 display:none 将其隐藏。然后,当需要使用这些图像时,可以使用 JavaScript 更改它们的显示属性以显示它们。
.preload {
  background-imageurl('image1.jpg');
  display:none;
}
const img = document.querySelector('.preload');
img.style.display = 'block';

注意事项:

  1. 不要过度使用:预加载应该只应用于大型图像和其他资源,而不应该预加载整个网站。

  2. 提供默认占位符:为了避免页面元素在加载期间出现的跳跃或闪烁,应该提供默认占位符,并在资源加载完毕后替换它们。

  3. 考虑SEO:与延迟加载类似,由于搜索引擎爬虫不会执行 JavaScript,因此如果使用预加载技术,请确保在标记中提供有关图像和其他资源的详细说明。

7. 基于响应式设计的图像显示

基于响应式设计的图像显示采用 CSS Media Queries 技术来检测用户设备的屏幕尺寸,并相应地调整图像大小和分辨率。这可以通过使用 srcset 和 sizes 属性以及 CSS 中的 max-width 和 min-width 属性来实现。其中,srcset 属性指定要使用的多个图像源,而 sizes 属性则告诉浏览器如何分配可用空间。然后,CSS 中的 max-width 和 min-width 属性确定了使用哪个图像源。

例如,以下是一个基本的 HTML 代码片段,演示如何使用 srcset 和 sizes 属性:

<img src="image.jpg" 
     srcset="image-320w.jpg 320w, 
             image-640w.jpg 640w, 
             image-1280w.jpg 1280w"
 
     sizes="(max-width: 320px) 280px, 
            (max-width: 640px) 600px, 
            1200px"
>

在上面的例子中,如果设备屏幕宽度小于或等于 320 像素,则使用图像 image-320w.jpg,如果设备屏幕宽度小于或等于 640 像素,则使用图像 image-640w.jpg,否则使用图像 image-1280w.jpg。

一些常见的方法来实现基于响应式设计的图像显示:

  1. 使用 srcset 和 sizes 属性:可以使用 srcset 和 sizes 属性来指定多个图像源并告诉浏览器如何分配可用空间。

  2. 使用 JavaScript:可以使用 JavaScript 来检测设备屏幕尺寸并相应地加载图像。

if (window.innerWidth <= 320) {
  document.getElementById('image').src = 'image-320w.jpg';
else if (window.innerWidth <= 640) {
  document.getElementById('image').src = 'image-640w.jpg';
else {
  document.getElementById('image').src = 'image-1280w.jpg';
}

注意事项:

  1. 图像优化:为了减少图像下载量和提高页面加载速度,应该对图像进行优化,如压缩、裁剪等。

  2. 浏览器兼容性:不是所有浏览器都支持 srcset 和 sizes 属性

8. 图像缓存

图像缓存通过将图像数据保存在浏览器或设备本地存储中来实现。当用户第一次加载页面时,浏览器会下载并缓存所有图像资源。然后,在用户再次访问相同页面时,如果资源没有发生更改,则浏览器会从本地缓存中获取图像数据,而不是再次下载它们。

图像缓存的优点

一些常见的方法来实现图像缓存:

  1. 使用 HTTP 缓存:可以使用 HTTP 头中的 Cache-Control 和 Expires 标头来指定图像的缓存策略。例如,可以将 Cache-Control 标头设置为 max-age=3600,表示浏览器应在 1 小时内缓存该资源。

  2. 使用 JavaScript:可以使用 JavaScript 来将图像数据保存在浏览器本地存储中。以下是一个基本的示例代码:

const img = new Image();
img.src = 'image.jpg';

// 将图像数据保存到本地存储中
localStorage.setItem('imageData', img.src);

// 当需要使用图像时,请从本地存储中获取它们
const imageData = localStorage.getItem('imageData');

注意事项:

  1. 缓存更新:由于缓存可能过期或内容发生更改,因此需要定期检查并更新缓存以确保用户看到最新版本的内容。

  2. 缓存清理:缓存可能会占用大量磁盘空间,因此需要定期清除缓存以释放空间。

  3. 安全性问题:由于缓存可能包含敏感信息或受版权保护的内容,因此必须采取安全措施来确保仅授权用户可以访问它们。

9. 预渲染图像

预渲染是一种在后台呈现图像的技术,以便在用户请求该图像时立即提供它。这意味着图像已经被呈现,并且在需要时可以立即使用。预渲染可以通过多种方式实现,包括使用 Prerender.io 或类似服务。

使用预渲染通常涉及将图像上传到 Prerender.io 或一个类似的服务。该服务将图像预渲染并准备好在需要时使用。然后,可以直接链接到该图像或将其嵌入到网站中。

<img src="https://i.prerender.io/https://example.com/image.jpg">

这会告诉 Prerender.io 在后台呈现目标图像,并在需要时提供它。

优点: 可以显著降低加载时间,因为图像已经被呈现并准备好在需要时使用。这可以改善用户体验,提高搜索引擎排名。

缺点: 涉及将图像上传到第三方服务。这可能会导致安全和隐私问题,并且可能需要支付费用。此外,如果网站中有很多图像需要预渲染,那么这将需要大量的存储空间和处理能力。