图片优化
本页内容
把一张原始尺寸的图片放进 src/ 或 public/,用 <img> 指向它,sitelo 就会缩放它、转成现代格式,并把标签重写为带 srcset 的版本。不用 import,也没有组件要学。
启用
export default {
images: true,
}编码工作由 sharp 完成,它是一个可选的 peer 依赖 —— 启用图片功能时请与 sitelo 一并安装:
npm install -D sharp不做图片优化的站点则完全不需要安装它。
写一个普通的 <img>
export default () => `
<html lang="zh">
<body>
<img src="/images/hero.png" alt="港口上的日出">
</body>
</html>
`import { html, body, img } from 'javascript-to-html'
export default () =>
html({ lang: 'zh' },
body(
img({ src: '/images/hero.png', alt: '港口上的日出' }),
),
)export default function Home() {
return (
<html lang="zh">
<body>
<img src="/images/hero.png" alt="港口上的日出" />
</body>
</html>
)
}一张 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× 屏幕上翻倍)。
你会得到什么
- 永不放大的缩放 —— 小于源图的每个配置宽度,再加上源图自身的宽度。一张 750px 的源图配合
widths: [400, 800, 1200],只会产出 400 和 750,到此为止。 - 现代格式 —— 只配一种格式会得到普通的
<img srcset>;两种或更多则会包进<picture>,并保留原格式作为回退。 - 没有布局偏移 ——
width和height会依据真实图片补全,同时加上loading="lazy"和decoding="async"。 - 开发时是同一套标记 ——
sitelo(dev)会重写页面并按需提供各个变体,你预览到的就是将来发布的样子。 - 共享缓存 —— 变体按内容哈希存放在
node_modules/.sitelo/images,因此开发和重复构建都不会把同一张图片编码两次。
重写作用于构建出的 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/**'],
},
}widths—— 默认[400, 800, 1200];源图自身的宽度永远是阶梯的顶端formats—— 默认['webp'];可选avif、webp、jpeg、pngquality—— 默认{ avif: 55, webp: 78, jpeg: 82 };按格式设置编码质量(png 用的是压缩级别,而非质量)sizes——sizes属性;标签上已有的sizes始终优先dimensions—— 默认true;补上width/heightlazy—— 默认true;补上loading和decodingexclude—— 要跳过的图片 URL 的 glob 或正则assetsDir—— 默认'assets/img'cacheDir—— 默认'node_modules/.sitelo/images'remote—— 默认false;在构建时优化https://图片prune—— 默认false;删除构建产物中已无人引用的原图dev—— 默认true;设为false可在开发时直接提供未处理的原图concurrency—— 并行编码数,默认 CPU 核数 − 1(最多 8);开启 remote 时为 1
两种格式会得到 <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">w— 像素宽度。任何宽度都行,不限于widths里的值;更小的源图保持自身尺寸,不会被放大。h— 像素高度。单独使用时宽度按比例跟随;与w一起使用时,图片会缩放填满该框并裁切——?w=200&h=200就是一个正方形缩略图。fit— 如何满足w×h的框:cover(默认)填满并裁切;contain把整张图缩放到框内,不裁切也不留边——width/height告诉你实际得到的尺寸。background— 把contain的结果补齐到精确的框,并隐含fit=contain。不带#的十六进制(fff、1a1a1a、ffffff80)、CSS 颜色名,或transparent——后者需要带 alpha 的格式;在 jpeg 里会变成黑色。position—cover裁切保留哪一部分。默认居中;某条边或某个角(top、left、right-bottom……),或 sharp 基于内容的entropy/attention策略。format—avif,webp,jpeg或png。单独使用时保留完整阶梯,只是换成这一种格式。
配置了多个 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="团队">entropy保留细节最多的区域——边缘、纹理、色彩变化。平坦的天空、空白的墙面和素净的背景最先被裁掉。适合风景、产品,以及任何“热闹”即“有趣”的图片。attention让裁切偏向饱和的色彩、高频的亮度变化和肤色——一种模拟人眼会看向哪里的启发式。适合人物照片:通常能找到脸和主体。
两者都只是简单的启发式——没有模型,没有训练——所以把它们当作面向大量图片的合理默认值,而不是对某一张的保证。对你在意的主视觉图,请直接指定边。选择只取决于图片本身,因此是确定性的,会像其他变体一样被缓存。
如何排除
已经带有 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 会清除已无人引用的本地原图。