标记稳定性:
背景 既然你想用手动圈选空间,进行绑定埋点,你需要一个唯一信息最为id来锁定具体的埋点载体。
定义
- 所谓的稳定本质上就是唯一且稳定的元素,避免当一个元素埋点后,随着业务迭代,避免埋点丢失的问题。
核心问题:
唯一:由所处文件位置和当前组件属性组合而成
稳定:主要还是依赖于当前元素一些属性决定,这样的话, 即便位置变动,也不影响
兜底:还是有序列值进行兜底处理
最终的目标
能够最终去实现,做到,只要组件本身不被替换,即便位置无论是相对,还是决绝对位置,发生了变化实际id不吧变化实际就是不希望产品,因为业务迭代,而导致每次都需要去重复的绑定。
历史记录:
早期调研大量业内实践大部分都是采用xpath来解决的的,但是最大的弊端在于,整它是基于DOM层级结果决定的,一旦相对的位置发生了变动就不符合预期
之后是分析归纳了,常用元素,然后结合bable定向匹配对应的属性,早期是通过解析属性的写法进行标记,依据策略模式进行对应处理
后期发现实际组件属性大部分为动态属性,大多为表达式,写法多样,所以最后进行优化,不再去关注具体写法,只关注最终运行时该属性值,通过手动构造AST,节点这个值就是开发者自己去绑定的属性
亮点:在于相比于市面上的纯粹用xpath会更加的稳定,原因在于不是依赖于所谓的dom层级结构,否则你调换一下组件相对位置整个可能都错乱了。
而目前主要的一个核心思路是绑定元素属性。
如果想再次拆解,现在已经明白了整体的一个思路,
基本理论在于:其实对于一个组件是否真正改变,应该是由其内部较为稳定的文案决定。
那就是目前整体方案分两类:
-
完全由内部嵌套的文案决定。
-
联合当前组件的属性。
目前由于整个插件体系是基于bable的:首先转AST语法树,对当前AST节点再次进行编译,为code.
bable有自己ast语法树规则,其ast节点定义就是一个是type还有一个是loc当前元素所处的位置。
代码逻辑:
// 核心逻辑:
if (expression && expression.type !== "JSXEmptyExpression") {
isGuarantee = true;
const buildRequire = template(%%value1%%.concat(%%value2%%));
const ast = buildRequire({
value1: type.stringLiteral(filedName),
value2: expression
});
dataMarkProp = types.jsxAttribute(
types.jSXIdentifier(markType),
types.jSXExpressionContainer(ast?.expression)
)
}
最核心的逻辑就是当涉及到表达式类似的属性的时候,我手动构造一个AST节点赋值给当前组件自定义属性上,不能直接把这个属性赋值,因为要遵循BABEL AST语法树规则。
这里面主要是运用template(%%value1%%.concat(%%value2%%)) 这个来进行构造两个属性
再利用 types.jsxAttribute , 去构造自定义属性,这个值就是当前表达式 eg:
const a = 1;
<Input value={a}/ >
<Input data-considerID=1>
这个就是里面最为核心精髓的点
以上的方案基本上就是这个属于零代码,但是覆盖率大概在70-80%一些极端非规范写法场景,依然无法覆盖,因为所绑定的属性依然有可能是重复的
装饰器
当初用装饰器主要是因为要准确上报对应信息点
这样做的目第是为了给组件加上装饰器,而不用动源码,符合开放封闭,非侵入式方案
可视化圈选SDK
定义问题
需要做到页面控件被触发后,需要发送对应信息给平台(对应的id,位置信息及触发类型),然后对其进行绑定,
三个核心问题
圈上后,怎么发射信息,怎么把这个信息或者说,其实就是内嵌iframe页面向外的平台发送信息
圈选能力,怎么圈选
元素id怎么去进行稳定。
业务逻辑
点击控件(能被圈选,发送的信息的前提是已经被唯一ID所标记)
控件发送信息到平台(绑定的id,元素类型)
高亮已圈选,未圈选
代码逻辑
主体是通过这个postmessage来进行通信
首先经历过这个标记引擎,后被标记上唯一Id
sdk初始化的时候,更具当前主题,来执行高亮对应的逻辑。目前主要包含了已绑定和未绑定的情况
当用户点击控件后,能够通过hover,click获取到当前目标元素,然后再经过包装的方法获取当前的元素位置信息。
再将当前sdk获取的信息,直接向平台侧发射