图片优化

把一张原始尺寸的图片放进 src/ 或 public/,用 <img> 指向它,sitelo 就会缩放它、转成现代格式,并把标签重写为带 srcset 的版本。不用 import,也没有组件要学。

启用

export default {
  images: true,
}

编码工作由 sharp 完成,它是一个可选的 peer 依赖 —— 启用图片功能时请与 sitelo 一并安装:

npm install -D sharp

不做图片优化的站点则完全不需要安装它。

写一个普通的 <img>

import { html, body, img } from 'javascript-to-html'

export default () =>
  html({ lang: 'zh' },
    body(
      img({ src: '/images/hero.png', alt: '港口上的日出' }),
    ),
  )

一张 3000×2000 的原图,出来是这样:

<img src="/assets/img/hero.a1b2c3d4-3000.webp"
     alt="港口上的日出"
     sizes="100vw"
     width="3000" height="2000"
     loading="lazy" decoding="async"
     srcset="/assets/img/hero.9f8e7d6c-400.webp 400w,
             /assets/img/hero.5b4a3c2d-800.webp 800w,
             /assets/img/hero.7c6d5e4f-1200.webp 1200w,
             /assets/img/hero.a1b2c3d4-3000.webp 3000w">

宽度不超过 400px 的屏幕拿到 400 的文件,不超过 800 拿到 800,不超过 1200 拿到 1200,更宽的则拿到完整的 3000——浏览器会选第一个能覆盖视口的档位(2× 屏幕上翻倍)。

你会得到什么

重写作用于构建出的 HTML,因此来自 src/ 和 public/ 的图片都会被覆盖到。

标签一旦被重写,就没有任何东西再指向原图,所以默认会把它从构建产物中清理掉。只有真正无人引用的文件才会被删:构建产物里的每个 HTML、CSS、JS、XML 和 JSON 文件都会被扫描,因此被 <a href>、og:image、RSS enclosure、CSS 的 url() 或 data-no-optimize 标签引用的原图都会保留。脚本在运行时拼出的 URL,或由服务端岛渲染出的 URL,它看不到——如果你有这类情况,请设置 prune: false。

选项

export default {
  images: {
    widths: [400, 800, 1200],
    formats: ['avif', 'webp'],
    quality: { avif: 55, webp: 78, jpeg: 82 },
    exclude: ['**/og/**'],
  },
}

两种格式会得到 <picture>

AVIF 更小但比 WebP 年轻,同时列出两者可以让浏览器自行选择,并为旧浏览器保留回退:

<picture>
  <source type="image/avif" srcset="/assets/img/hero.*-400.avif 400w, ..." sizes="...">
  <source type="image/webp" srcset="/assets/img/hero.*-400.webp 400w, ..." sizes="...">
  <img src="/assets/img/hero.*-3000.png" alt="..." srcset="..." width="3000" height="2000">
</picture>

按名字取一个尺寸

轮播缩略图、头像、卡片网格——有些图片永远只以一个尺寸展示,整条阶梯对它们是浪费。变体文件名带有内容哈希,无法手写;改为在 URL 里写明宽度,标签就会只带上那一个文件:

<img src="/images/hero.png?w=400" alt="港口上的日出">
<img src="/images/hero.png?w=400&format=jpeg" alt="港口上的日出">
<img src="/images/hero.png?w=200&h=200" alt="港口上的日出">
<img src="/images/hero.png?w=200&h=200&fit=contain" alt="港口上的日出">
<img src="/images/hero.png?w=200&h=200&background=fff" alt="港口上的日出">
<img src="/images/hero.png?w=200&h=200&position=top" alt="港口上的日出">
<img src="/assets/img/hero.9f8e7d6c-400.webp"
     alt="港口上的日出"
     width="400" height="267"
     loading="lazy" decoding="async">

配置了多个 formats 时,固定宽度仍会得到 <picture>,每个 <source> 一个文件。参数名与 vite-imagetools 相同。远程 URL 从不解析——查询串属于源站——而关闭 images 后,静态主机会忽略查询串并返回原图。

让图片自己决定裁切

边或角是可预测的,但一组各不相同的轮播照片,主体很少两次都在同一个位置。entropy 和 attention 让 sharp 逐张查看图片,把裁切窗口滑到它认为值得保留的地方:

<img src="/images/hero.png?w=300&h=300&position=entropy" alt="港口上的日出">
<img src="/images/team.jpg?w=300&h=300&position=attention" alt="团队">

两者都只是简单的启发式——没有模型,没有训练——所以把它们当作面向大量图片的合理默认值,而不是对某一张的保证。对你在意的主视觉图,请直接指定边。选择只取决于图片本身,因此是确定性的,会像其他变体一样被缓存。

如何排除

已经带有 srcset、位于 <picture> 内部,或指向 SVG、动图 GIF、远程 URL 的标签都不会被改动。其他情况请显式声明:

<img src="/images/exact.png" alt="像素画" data-no-optimize>

社交卡片和 favicon 的图片写在 <meta> 和 <link> 里,这里永远不会重写它们 —— 它们的 URL 保持固定。

远程图片

从 CMS 导入的内容往往指向别人的服务器。开启 remote,这些图片就会被下载、优化,并从你自己的域名提供:

export default {
  images: {
    remote: true,
  },
}

抓取失败时标签会原封不动地保留,并给出警告 —— 不稳定的源站永远不会让你的构建失败。随后 prune 会清除已无人引用的本地原图。