应优先截取有明确宽高的最外层容器(如#app),而非document.body;需确保元素渲染完成、配置usecors和scale等参数,并注意跨域图片与字体问题。

用 html2canvas 截当前页,但别直接截 body
多数人一上来就 html2canvas(document.body),结果截图空白、样式错乱、字体不显示——根本原因是它不真正渲染页面,而是靠 CSS 解析 + canvas 重绘,对现代布局(Flex/Grid)、跨域图片、@font-face、伪元素支持极弱。
实操建议:
- 优先截最外层有明确宽高的容器,比如
document.getElementById('app')或document.querySelector('.page-root'),避免全页滚动区域 - 确保目标元素已完全渲染:在
Vue.nextTick、React.useEffect(() => {}, [])或setTimeout(..., 100)后调用 - 显式传参控制质量:
{ useCORS: true, allowTaint: false, scale: 2, logging: false };scale: 2能缓解高清屏模糊,但会显著增加内存占用 - 跨域图片必须服务端加
Access-Control-Allow-Origin,前端设useCORS: true才能加载,否则留白
Chrome DevTools 里按 Ctrl+Shift+P 截图最准,但不能自动化
这是目前唯一能真实还原浏览器渲染结果的方式(基于 Blink 引擎快照),支持阴影、滤镜、SVG、WebGL 纹理等所有视觉效果。
使用场景:人工验证、临时存档、调试布局。
常见错误:按完快捷键后没选对范围,直接回车会截整个 viewport(含滚动不可见部分);想截完整长图得输入 Capture full size screenshot 并回车。
注意:
- 不能截 iframe 内容(除非同源且已加载完成)
- 不会执行 JS 动画帧,动态图表可能卡在某一帧
- 保存路径由浏览器默认下载目录决定,无法用脚本指定位置
服务端用 Puppeteer 截图,适合批量/定时任务
Node.js 脚本启动 Chromium 实例,走真实渲染流程,准确率接近 DevTools,且可编程控制。
关键配置差异:
-
page.screenshot({ fullPage: true })截长图,但性能差、内存涨得快;更稳的做法是clip: { x, y, width, height }分段截再拼接 - 务必等资源加载完:
await page.waitForNetworkIdle({ timeout: 5000 })比单纯page.goto(url, { waitUntil: 'networkidle2' })更可靠 - Linux 服务器需装字体库,否则中文变方块:Debian/Ubuntu 上跑
apt-get install fonts-wqy-zenhei,并用launch({ args: ['--font-render-hinting=none'] })减少渲染差异 - 生成 PNG 比 JPEG 大 3–5 倍,但无损;如需压缩,先截 PNG 再用
sharp转 WebP
dom-to-image 不推荐用于复杂页面
它和 html2canvas 类似,走 DOM 解析 + SVG 渲染路径,但对 CSS 支持更差:不支持 transform、filter、mask,连 position: sticky 都会错位。
只适合纯静态、语义化 HTML(比如后台管理系统的简单报表卡片)。
容易踩的坑:
- 异步加载的图片不会等待,得手动
Promise.all(imgs.map(img => img.decode())) - 返回的是
data URL,大图直接img.src = dataUrl可能触发浏览器内存限制(尤其 Safari) - 不兼容 Shadow DOM,自定义元素内部内容全丢
立即学习“前端免费学习笔记(深入)”;
真正难的不是选哪个工具,而是判断“要截什么”:是给用户看的最终效果?还是调试用的结构快照?前者必须用真实渲染(Puppeteer / DevTools),后者才考虑轻量方案。另外,所有客户端截图库都绕不开跨域和字体问题,这两点漏掉一个,截图就废一半。










