SnapDOM v3 功能特性全解析:捕获、导出、插件与缓存机制技术指南
【免费下载链接】snapdomHigh-performance engine for capturing, modifying, and converting DOM elements into any format.项目地址: https://gitcode.com/GitHub_Trending/sn/snapdom
导读
本文是 SnapDOM v3 的官方技术特性参考,全面覆盖从 DOM 捕获克隆、样式快照、资源内联,到多格式导出、插件系统、缓存与跨浏览器处理的完整技术栈。SnapDOM 在浏览器中捕获渲染后的界面状态,核心产物是可复用的图像与 canvas 导出,插件则进一步提供 HTML、结构化上下文、元素地图、PDF 与录制能力。读完本文,你将掌握 SnapDOM 每个核心能力的行为边界、关键配置项的语义与默认值,以及它们背后的源码实现依据,能够据此为实际项目做出正确的捕获方案选型。
捕获与克隆(Capture & Clone)
SnapDOM 的核心流程是:克隆选中的子树 → 快照计算样式 → 内联子树所需资源,最终产出一个自包含、可独立渲染的克隆。整个管线在 src/core/capture.js 中实现,入口captureDOM负责element → clone,渲染引擎再负责clone → pixels。
snapdom.fromString()提供了一条脱离页面挂载的捕获路径:它接受一段 HTML 字符串,将标记离屏挂载到当前文档(因此页面 CSS 与字体同样生效),完成捕获后自动清理。需要注意其安全边界——标记会经innerHTML写入真实文档并按页面标记的方式激活,<img onerror>等内联处理器会在调用方源中执行,因此传入前必须自行消毒不可信的 HTML。该行为在 src/api/snapdom.js 中有明确注释:这不是可修复的 bug,而是捕获需要真实布局的必然代价,安全责任在调用方。单根元素时该元素即捕获目标,否则以包装层为目标;混合文本片段(如Hello <strong>world</strong>)会保留整体包装。另外fromString强制burst: false,因为一次性挂载销毁后无法再被复用。
各类内容的捕获行为
| 内容 | 捕获行为 |
|---|---|
| 开放 Shadow DOM | 展平 shadow root,作用域化样式并解析分配的 slot |
| 同源 iframe | 连同其自身文档中的资源与字体一起捕获 |
| 跨域 iframe | 使用有尺寸的占位符;placeholders: false时使用不可见的间隔占位 |
| Canvas | 捕获当前位图及固有尺寸与 CSS 尺寸 |
| Video | 在可读时捕获当前帧;否则回退到 poster |
| 音频控件 | 使用绘制的播放器表示 |
| 表单控件 | 保留当前值、checked/indeterminate 状态、选区及状态相关样式 |
| 图片与 picture | 冻结选中的响应式源与渲染尺寸,包括object-fit与object-position |
| 内联 SVG | 保留绘制样式并内联引用的外部定义与 symbol |
| 滚动容器 | 保留滚动位置、裁剪与定位后代 |
核心会遮罩密码框;其他可见输入值默认保持可见,除非插件或排除规则将其移除。Firefox 的复选框与单选钮使用绘制的替代物(原生 SVG 渲染差异所致)。scripts、template等不渲染的节点会被跳过;克隆根的外边距会被中和(对应 capture.js 中的neutralizeRootMarginCollapse,修复 #426),屏幕外content-visibility内容会被强制可用以完成捕获。
样式处理(Styles)
样式的核心机制是:计算样式被复制并去重为生成的类(class 级联),作者!important规则与内联优先级得以保留。在 src/core/capture.js 中,prepareClone负责克隆与样式快照,styleShareSafe决定是否启用同一性共享快速路径(identity-share),当样式表不可扫描或存在运行中的动画时自动回退到完整读取。
- 伪元素、文本装饰、文本描边、字体变体与 OpenType 设置均被捕获。
- 伪元素内容中的 CSS 计数器支持 reset、increment、嵌套与 counter 样式。
- 行数截断(line clamping)与省略号(ellipsis)会在 SVG 渲染需要处转换为捕获文本(
lineClampTree,见 src/core/capture.js)。 - 根变换包含独立的
translate、rotate与scale,并做 origin-aware 边界计算。根平移被归一化;outerTransforms: false还会移除旋转,但保留scale与skew。 outerShadows: false移除根的阴影、轮廓与drop-shadow(),但保留 blur;true包含根效果;'subtree'额外包含后代绘制到根盒之外的阴影墨迹。显式裁剪边界永不扩展。- 背景、遮罩、border-image、clip-path 与混合模式均保留其捕获样式。
- 捕获路径支持时包含自定义滚动条样式;滚动内容本身按不含自定义滚动条的状态捕获。
reconcile: true会在文档中测量已样式化的克隆并与源比对,修正发散盒子,可修复文本换行或布局差异,代价是额外一次测量往返(注释明确为 roughly doubles capture time)。excludeStyleProps通过 RegExp 或谓词排除指定属性(如/^--/排除 CSS 变量,见 src/core/context.js 中 #348 的说明)。
图片与背景(Images & Backgrounds)
图片、SVG 图片引用、CSS 背景层、遮罩与 border-image 都会被内联。多层背景保留定位、尺寸、重复与混合设置;image-set()候选项跟随页面的设备像素比。响应式图片使用选中的源而非回退的src;常见懒加载属性也会被解析。图片抓取共享进行中的请求并缓存资源。
useProxy:支持代理 URL 或模板,用于跨域资源抓取。fallbackURL:为加载失败的图片提供替换资源。
内联的栅格图片会被降采样到捕获所需的分辨率,尽量保留源编码格式,且仅当更小的图片确实节省字节时才采用(compress机制,见 src/core/capture.js 中compressCloneAssets)。更大尺寸的导出可以恢复捕获时的原始图——它不会重新读取实时图片的后续版本(即"冻结原图"语义:restoreCompressedAssets从__compressedSnapshot恢复)。
Canvas 尺寸受浏览器安全限制约束,超大的导出可能被降采样并给出警告。
字体与图标字体(Fonts & Icon Fonts)
embedFonts: 'auto'(默认值)只嵌入捕获子树实际使用的文档声明的 Web 字体;系统字体页面的捕获跳过整个嵌入阶段(零成本)。true与false则显式启用或禁用 Web 字体嵌入。字体管线的两半在 src/modules/fonts.js 中:collectFontUsage遍历子树一次,记录每个使用的 family/weight/style/stretch 以及展示的每个码点;embedCustomFonts扫描文档样式表(同源经 CSSOM、跨域抓取、嵌套@import展平),保留匹配使用变体且命中使用码点的 face,并把所有url()转为 data: URL。
| 选项 | 用途 |
|---|---|
localFonts | 提供带 family 与 source 的字体描述符,可附加 weight/style/stretch |
excludeFonts | 排除指定字体家族、域或子集 |
fontStylesheetDomains | 向跨域样式表扫描添加域 |
iconFonts | 用家族名或模式扩展图标字体检测 |
被识别的图标字体会渲染为图片(包括连字图标),因为图标字体从不嵌入——SVG 内部无法加载其字形。资源访问仍取决于浏览器权限与 CORS。仅通过 JavaScript 创建的字体可能需要显式提供localFonts源(CSSOM 不可见)。
导出格式(Export Formats)
一次捕获结果可以被多次导出而无需重新克隆实时元素。所有导出方法由 src/api/snapdom.js 的buildResult统一构建:核心导出器按类型惰性加载,导出调用按结果串行排队(runExport内部_exportQueue),afterSnap在首次成功导出后触发一次。
| 方法 | 返回 |
|---|---|
toRaw()/url | SVG data URL |
toSvg() | SVG 支撑的HTMLImageElement |
toCanvas() | HTMLCanvasElement |
toBlob() | SVGBlob,除非捕获或导出显式选择格式 |
toPng() | PNGHTMLImageElement |
toJpg()/toJpeg() | JPEGHTMLImageElement |
toWebp() | WebPHTMLImageElement,浏览器无法编码 WebP 时回退 PNG |
download() | 下载请求的格式 |
to(name, options?) | 调用核心或插件导出器 |
静态快捷方式包括snapdom.toPng(element)、snapdom.toCanvas(element)、snapdom.download(element)等,它们内部均先执行snapdom(el, options)再调用对应导出方法。toImg()为兼容性保留,SVG 图片请使用toSvg()。JPEG 与 WebP 默认白色背景(normalizeExportOptions中当无背景色时强制#ffffff)。iOS 上下载可使用 Web Share API。
重要语义:SVG 捕获将 HTML 放在<foreignObject>内,非浏览器 SVG 查看器可能无法渲染。result.meta暴露冻结的捕获几何信息(w0/h0/vbW/vbH/targetW/targetH/contentX/contentY),用于叠加层与图片定位;result.warnings报告捕获诊断(如reconcile-risk、shrink-failed)。
选项系统(Options)
每个公开选项在 src/core/context.js 的createContext中完成归一化——默认值、别名与编译后的排除策略只在此处决定,因此新增选项也是从这里开始。
- 尺寸:
width/height优先于scale;dpr乘以像素尺寸。注意 v3 规则:width/height是绝对输出尺寸,两者任一设置时scale被忽略并给出调试警告。 - 内容:
filter/filterMode与exclude/excludeMode可同时使用且模式独立;clip('viewport'或页面坐标矩形)与captureSelection进一步控制捕获内容。排除优先于过滤(shouldExclude中先判 exclude 再判 filter)。 - 渲染:
backgroundColor、quality(默认 0.92)、outerTransforms、outerShadows、reconcile控制输出。 - 资源:字体选项、
useProxy、fallbackURL、placeholders控制内联与回退。 - 复用:
canvas接受现有渲染目标(循环捕获时避免整画布拷贝,见 #494 的 iframe canvas 处理);invalidate在无浏览器信号的更改后刷新。
插件系统(Plugin System)
插件可以修改克隆、解析源节点或定义导出器。它们通过snapdom.plugins(...)全局注册,或通过plugins选项局部传入。局部插件先运行,并按名称覆盖全局插件(attachSessionPlugins的 local-first 语义,插件导出覆盖核心导出)。
| 官方插件 | 输出或效果 |
|---|---|
html-export | 带内嵌样式与字体的捕获 HTML |
context-export | 文本大纲或 JSON 页面上下文 |
agent-map | 带角色、名称、状态与边界框的图片和元素地图 |
pdf-image | 含 JPEG 捕获的可下载 PDF |
gif-export/video-export | 实时元素随时间变化的录制 |
ascii-export | 图片的文本表示 |
filter/color-tint/timestamp-overlay | 对捕获克隆的视觉改变 |
replace-text/redact-inputs | 文本替换与表单值遮罩 |
官方插件的安装与完整参数见 packages/plugins/README.md(如filter的preset、gif-export的fps/duration/maxColors、agent-map的fields/semantic/maxImageWidth等)。
阶段(Stage)与needs
捕获是一条链:element → [clone] → [render] → exports。每个插件声明自己需要走多远(needs: 'clone' | 'render',默认'render'),捕获运行到所有附着插件声明的最大深度(src/core/stages.js 的resolveStage)。声明needs: 'clone'的插件可跳过渲染,此时核心图片导出不可用(url、toPng()等访问会抛出错误,指明是哪些插件降低了阶段——因为"重新捕获"将返回另一个瞬间的像素,克隆本身就是冻结),而插件仍可返回自己的数据。全局注册拒绝 clone-only 插件(阶段降低只允许 per-capture)。未知的needs值会直接抛错而非静默取默认。
影响捕获的钩子(render 相关)会禁用自动记忆化,除非插件声明pure: true;仅导出型插件保留记忆化。导出调用按结果串行化;afterSnap在首次成功导出后触发一次。核心导出在 src/api/snapdom.js 中与插件导出合并:插件同名覆盖核心。
缓存与记忆化(Caching & Memoization)
v3 的性能核心是burst 记忆化(src/core/burst.js):符合条件的未变化元素从首次捕获复用结果;安全的局部变化可重建受影响子树(差异路径,diff.js);不确定的变化走完整管线。Canvas、video、iframe 与已识别的动画总是新鲜捕获。
- 函数值的
filter、exclude、excludeStyleProps、fallbackURL需要新鲜捕获(回调可能读取外部状态,身份不变也不代表结果不变)。适用回调决策(包括样式与回退决策)在每次新捕获时重新评估;改变闭包无需invalidate;已返回的结果保留其原始捕获状态。 - DOM 变更、交互状态、滚动、尺寸变化与资源事件会使相关缓存失效。程序化 CSSOM 编辑(如
sheet.insertRule())没有变更信号,需为下一次捕获传入invalidate: true。 - 资源/样式缓存与结果记忆化相互独立。
cache: 'soft'是默认(normalizeCachePolicy将遗留的'auto'、'full'映射到它);cache: 'disabled'或false会清空并绕过持久缓存,供调试使用。
记忆化引擎维护一个作用域MutationObserver与每元素最近结果缓存,采用有界 64 条目的 LRU(超出后最久未服务的 memo 被拆除,其元素下次新鲜捕获)。失效矩阵覆盖 DOM 变更、滚动(composed:false 事件)、表单控件属性写入、:hover、窗口 resize、<head>CSS、同 tick 的<style>、CSS/WAAPI 动画(运行中绝不服务也不持久化 memo)、以及捕获中途的外部编辑(hasTornMutation净效果判定)。开放 shadow root 各自配备一个 observer 与局部事件监听,因为MutationObserver不跨 shadow 边界。
snapdom.preCapture()按意图预准备捕获(src/api/preCapture.js):它学习在某个控件的 press/click 事件期间开始的合格捕获,然后在后续 pointer-enter 或 focus 事件上重复执行。它不接受参数、不轮询页面;在掌握任何控件之前,首次意图事件会捕获可见视口一次并丢弃,用于预热样式快照、字体与图片(源码注释记录的实测数据:预热后可见元素捕获为稳态的 1.1x,冷启动为 3.3x)。
跨浏览器处理(Cross-browser Handling)
- Safari 的 SVG 图片路径会等待内嵌图片与字体绘制完成才返回栅格结果(见 src/api/snapdom.js 中 Safari pre-step:等待实际使用的字体就绪,并"poke" GPU 支撑的
<canvas>存储,避免toDataURL空白——WebKit #219770)。 - Firefox 表单控件在原生 SVG 渲染不同处使用替代物。编码与 canvas 尺寸限制仍取决于浏览器。
SnapDOM 有两个渲染引擎。SVG 是默认引擎(src/engines/svg.js:base reset、bbox 与 bleed 数学、foreignObject 组装、SVG data URL 编码,还会对重复内联样式做属性选择器 intern 优化)。第二个是html-in-canvas(通过浏览器原生 canvas API 绘制同一个完成的克隆),以engine: 'html-in-canvas'选择。该引擎仍属实验性质,需要兼容浏览器且启用其 canvas 绘图标志;构建时需SNAPDOM_CANVAS_ENGINE=1包含(默认构建仅含 SVG),不支持的捕获回退到 SVG。
上述导出表描述的是 SVG 捕获。原生画布捕获成功时产生位图:url与toRaw()返回 PNG data URL,toSvg()与toImg()返回 PNG 支撑的图片,toBlob()默认 PNG;显式请求 SVG Blob 会失败,因为该引擎不序列化 SVG。位图被延迟 mint(首次读取时才编码 PNG,避免像素导出付出 15–22 倍的编码成本)。
节点级控制属性(Node-level Control Attributes)
| 属性 | 效果 |
|---|---|
data-capture="exclude" | 使用所选excludeMode排除节点 |
data-capture="placeholder" | 用占位符替换它 |
data-placeholder-text | 设置占位符文本 |
data-capture="exclude"在shouldExclude(src/core/context.js)中优先于一切选择器与谓词判定。SnapDOM 自身的捕获脚手架(markInternalNode标记的内部节点,如fromString的挂载层与#snapdom-sandbox)总是被排除,不会出现在任何导出中。
实践建议小结
- 安全第一:
fromString只接受已消毒的 HTML;html-export输出保留事件处理属性的原始标记,也不做消毒。 - 性能:默认
burst记忆化与cache: 'soft'开箱即用;高频轮询界面直接复用捕获结果。若存在 CSSOM 编辑或关闭的 shadow root,使用invalidate: true。 - 忠实度:文本换行不确定时启用
reconcile: true;根阴影与变换按需用outerShadows/outerTransforms控制;敏感表单值用redact-inputs插件或data-capture="exclude"处理。 - 扩展:仅需结构化数据时用
needs: 'clone'插件(如contextExport、agentMap),避免无谓渲染;但注意此时url与图片导出不可用。
相关深入资料:插件钩子完整契约见 PLUGIN_SPEC.md,官方插件参考见 packages/plugins/README.md,架构总览见 ARCHITECTURE.md,社区插件贡献指南见 CONTRIBUTING_PLUGINS.md。
【免费下载链接】snapdomHigh-performance engine for capturing, modifying, and converting DOM elements into any format.项目地址: https://gitcode.com/GitHub_Trending/sn/snapdom
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考