@ 标注/ 元素选择器
由自动抓取SDK插件所消耗的共享元素选择算法,app.amplitude.com标记UI,和Chrome扩展可视标记器. 一个执行,三个表面,输出相同.
为什么它存在
遗产自动抓取 cssPath 算法基于它找到的任何元素 id, 它打破了选择器稳定性 当一个框架生成像 id 一样的 id :r5: (反应) useId)或(或) radix-A1B2C3 (Radix UI) (英语). 图书馆也非常依赖各种课程, 以进行兄弟间的脱节 swiper-slide-active 或者说 Mui-selected。 。 。 .
此软件包将算法替换为策略链管弦乐器,将自动生成的ID和库状态类从锚选择中过滤出,然后在位置上通过 :nth-of-type。结果:在组件再起时幸存的选取器、旋转木马的相互作用、悬浮/焦距和小设计扭矩.
见设计文件 packages/plugin-autocapture-browser/element-selector-strategy-v1-no-classes.md 关于全部背景和理由.
安装
这个包裹住在 Amplitude-TypeScript 单相并发布为 npm @amplitude/element-selector. 消费者在此回购中宣布它为工作空间依赖:
{
"dependencies": {
"@amplitude/element-selector": "workspace:*"
}
}
唯一的运行时间依赖是 tslib; 诊断器接受任何记录器 warn 财务报告和审定财务报表 debug 方法。 。 .
使用量
独立消费者
import { createSelectorEngine, resolveSelectorConfig } from '@amplitude/element-selector';
// Start with the documented defaults. Remote-config values can be applied as
// they arrive — `resolveSelectorConfig` is safe to call repeatedly.
const config = resolveSelectorConfig({ enabled: true });
const engine = createSelectorEngine(config);
// Generate a selector for any element. The engine returns the best selector
// it can produce, falling back to a positional walk when no strategy applies.
document.addEventListener('click', (event) => {
const target = event.target as Element;
const selector = engine.generate(target);
console.log(`Clicked: ${selector}`);
});
自动抓取 SDK 插件
自动抓取插件将每台引擎跳出一个 init() 调用, 线程 loggerProvider 用于诊断日志,并通过 updateConfig数字 :
import { createSelectorEngine, resolveSelectorConfig } from '@amplitude/element-selector';
const engine = createSelectorEngine(resolveSelectorConfig(remote, config.loggerProvider), {
logger: config.loggerProvider,
});
config.remoteConfigClient.subscribe(
'configs.analyticsSDK.browserSDK.autocapture.elementSelector',
'all',
(next) => {
engine.updateConfig(resolveSelectorConfig(next, config.loggerProvider));
},
);
Chrome 扩展可视标记器
扩展读取页面已启用的引擎 window.amplitude.elementSelector (当存在时)和订阅 onConfigChange 因此它的预览会和客户的运行时间配置同步 :
const pageEngine = (window as any).amplitude?.elementSelector;
if (pageEngine) {
const unsubscribe = pageEngine.onConfigChange((nextConfig) => {
rerenderTaggingUI(nextConfig);
});
// Call unsubscribe() on extension teardown.
}
算法如何运作
管弦乐团从被点击的元素走出,从它的祖先上行,按优先顺序在每个级别尝试每个策略. 第一种产生独一解析选择器的策略取胜;锚与原目标之间元素的踪迹被表示为位置下移.
战略链
| 顺序 | 战略 | 锁定格式 | 适用时 |
|---|---|---|---|
| 1个 | explicitTrackingAttribute | [data-amp-track-id="value"] | 客户设置非空值的跟踪属性 |
| 2个 | stableId | tag#id | 元素有ID, ID没有被压制, 它不符合任何自动生成的 ID 模式 |
倒计时
当两个策略都没有在行走的任何地方产生一个独特的选择器时,引擎会引用 fallbackCssPath后退从目标后退到 <html>,将每个步骤表示为 tag:nth-of-type(n) 并提前终止于第一个在自动生成的 id 过滤器中存活下来的独特 id .
兄长之间的矛盾: :nth-of-type从不上课
倒计时者不使用班级来进行兄弟间的分化. 班级在现代框架中是运行时间波动的——Tailwind 公用事业随着设计调整而改变,CSS-in-JS 散列改变每个建筑,以及图书馆状态班级(swiper-slide-active, (中文(简体) ), is-open, (中文(简体) ), Mui-selected) 当用户交互时在元素之间移动。 经由 :nth-of-type(n) 在所有这些方面都是稳定的.
默认
enabled: false
v1舰沉没. 自动抓取继续使用遗留 cssPath 直到远程配置有效载荷翻转 enabled 改为 true 给一只猩猩.
explicitTrackingAttribute: "data-amp-track-id"
客户可以将这个属性设定在一个元素上,以明确地将选择者固定在它上. 设置为空字符串是一种压制信号——引擎将元素的ID当作不存在.
autogeneratedIdPatterns
默认列表过滤出看起来生成而非作者的 ID :
| 图案 | 接头 | 示例 |
|---|---|---|
^:r[0-9a-z]+:$ | 反应 useId | :r5:, (中文(简体) ), :rk: |
^radix- | Radix UI 原始语 | radix-A1B2C3 |
^headlessui- | 无头UI组件 | headlessui-menu-button-:r5: |
^mui- | MUI 内部 id 前缀 | mui-12345 |
^[a-f0-9]{16,}$ (单位:千美元)i(中文(简体) ) | 无连字符的 UUID, 构建散列 | a9b8c7d6e5f4321b |
-\d{8,}$ | 跟踪长位数运行 | session-1700000000 |
\d{4,} | 任意 4+ 连续数字 | product-2134, (中文(简体) ), row-1234 |
unstableClassPatterns
默认列表(被倒回所使用) 捕获在名称上看起来稳定但实际操作上并不稳定的类别 – Tailwind 公用事业, CSS- in-JS hashes (Emotion, 有样式组件, CSS 模块, 有样式- jsx), 和库运行时状态类( Swiper, MUI, Radix, BEM 风格) is-active) (中文(简体) ).
maxAncestorWalkDepth: undefined
{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}默认行走会一直到 <html>. 设定为正整数以限制深度——对灾难性地深度的DOMS有用,因为独特性检查可能变得昂贵.
阴影DOM
阴影支持独立于选择器的引擎杀死开关通过 shadowDomEnabled 财务报告和审定财务报表 maxShadowDomDepth 在远程配置。 默认 : shadowDomEnabled: false, (中文(简体) ), maxShadowDomDepth: 1 (宣布为 [1, 10]) (中文(简体) ).
当启用时, engine.generate 发出已划定的选择器( e)>>> (在每棵树之间) resolveSelector - 简单 document.querySelector 无法穿透阴影界限.
自动抓取插件在每页通过接合器支持一次( )ShadowGate:第一个能够使其生效的远程配置交付,用于其余的页面负载. 后期发送会禁用阴影支持,直到下次导航时才会关闭.
全面设计文件(建筑、性能、后期情况、外勤部缓解、后续): packages/plugin-autocapture-browser/SHADOW-DOM.md
推出限制(摘要)
| 限制 | 影响 |
|---|---|
| 只打开阴影根 | 封闭的根是不透明的. composedPath() 将事件重定向到主机; 选择器不能通过闭合根进行回转. |
| 每页批量 | 残疾 shadowDomEnabled 中会话在下一个整页负载之前没有效果。 第一发炮也修补 maxShadowDomDepth 为页面. |
迟到 attachShadow | DOM中已存在的元素所附带的根是观察者不发现的. 点击仍然抓取; 突变/曝光可能错过 。 全球 attachShadow 补丁是 未计划 ——会话重放和其他 libs 已经包裹了相同的 API(细节) (中文(简体) ). |
| 深度预算 | 目标比 maxShadowDomDepth 为预算最外层的阴影主机制作一个最佳选择器. |
| 倾角盖 | MAX_SHADOW_COMPOSED_WALK_ITERATIONS (祖先行走)和 MAX_SHADOW_DOM_TRAVERSAL_NODES (Shadow-root Collection DFS)将病理树的同步工作捆绑起来. |
预推出: 运行 e2e/shadow-dom.spec.ts (国际中心)和 e2e/shadow-dom-perf.spec.ts 在启用 org 旗帜之前, 本地.
远程配置
远程配置有效载荷形状定义于 src/types.ts 作为 ElementSelectorRemoteConfig 作为JSON Schema的镜子 schema/element-selector-remote-config.schema.json 用于仪表板的远程配置编辑器和后端验证.
完全由客户提供模式清单 替换 默认的——它们不是合并的. 空数组( E)autogeneratedIdPatterns: [])是一个明确的"无法过滤"信号,而不是"使用默认"信号. 无效的 regex 字符串被丢弃( 带有警告日志) 而不是崩溃引擎 .
诊断
捕获出错的每个路径都接受可选 ElementSelectorLogger这在结构上与SDK兼容 @amplitude/analytics-core 日志提供者。 接通后,发动机会浮出水面:
warn— 远程配置中的 regex 字符串无效, 用户例外updateConfig风扇出debug——因战略而产生缺陷的选择者
在不提供伐木机时,引擎会保持沉默——为不想要诊断噪声的消费者保留防火和遗忘语义.
交互式测试台
在本地运行软件包的测试台页面,以点击引擎周围的Cononic情景,实时编辑远程配置有效载荷,并观看诊断日志输出:
pnpm --filter @amplitude/element-selector testbed
# → builds the package, serves the testbed at http://localhost:4567
试床住在 packages/element-selector/testbed/ 并进口真正的已建引擎 lib/esm/index.js ——没有平行执行可以从套装中漂移. 每一个场景来自 test/scenarios/ 。单击任意元素,显示已发射的选择器、胜出策略以及是否 querySelector 绕行返回被点击的元素.
测试
pnpm --filter @amplitude/element-selector test
pnpm --filter @amplitude/element-selector lint
pnpm --filter @amplitude/element-selector build
测试套房包括:
- 初级单位测试 用于图案包、助手、策略、配置解析器、管弦器、倒置和发动机厂
- 司马出行后卫 - 声称JSON Schema和
ElementSelectorRemoteConfig类型脚本保持同步 - 假设程序 - 参数化差分测试,将引擎运行在手取的固定装置之上(Swiper、MUI、Tailwind、语义HTML、CSS-in-JS汤等),并声称预期选择器和a
querySelector往返 - 财产检验 ——生成随机 DOM 树并配有混合稳定/自動代碼和分级汤,断言引擎总是会向目标喷出一个独特的解回选择器
设计参考
- 设计文件 :
packages/plugin-autocapture-browser/element-selector-strategy-v1-no-classes.md - 实施计划:设计文件"滚出"部分分阶段PR分解