V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  aryu  ›  全部回复第 2 页 / 共 2 页
回复总数  22
1  2  
2019 年 1 月 4 日
回复了 autoxbc 创建的主题 JavaScript 我猜很多人都没真的用过 MutationObserver
例子里面的打印不是很有利于观察实际发生的情况,我稍微修改了一下,增加了每次 callback 触发的打印和遍历 added node 时具体的 node 的打印。

```
<!DOCTYPE html>

<head>
<script>
const callback = mttns => {
console.log('callback here')
mttns.forEach(mttn => {
[...mttn.addedNodes].forEach(node => {
if (node.nodeType !== 1)
return;

console.log('added element type node', node)
if (node.querySelector('div p')) {
console.log('[found with selector "div p"]');
}
if (node.querySelector('div a')) {
console.log('[found with selector "div a"]');
}
});
});
};
const opts = { childList: true, subtree: true };
new MutationObserver(callback).observe(document, opts);
</script>
</head>

<body>
<div>
<p></p>
<script></script>
<a></a>
</div>
</body>

</html>
```

MutationObserver 的核心机制是“异步 + 批量”,主要是处于性能考虑,不过在使用上是容易造成一些误解。改动后的代码打印结果如下:

```
>>> callback here
added element type node <body>​…​</body>​
[found with selector "div p"]
added element type node <div>​…​</div>​
[found with selector "div p"]
added element type node <p>​</p>​
added element type node <script>​</script>​
>>> callback here
added element type node <a>​</a>​
```

可以看到以下现象:
1. 总共产生了两次 callback
2. 第一次 callback 添加了 body, div, p, script 四个 element node
3. 第二次 callback 添加了 a 这一个 element node
4. 如果你调整 script 标签的位置,你会发现 script 结束会立即触发一次 callback,这可能和 MutationObserver 具体的实现规则有关。

所以实际发生的情况是当第一次 callback 触发时,由于是异步的,所以第一次 callback 内包含的 4 个新增 node 已经存在于 DOM 中,query 'div p' 是可以查询到的,但是 a node 还没有插入,所以此时查询不到。
第二次 callback 时 a node 单独被插入,但是也不符合 'div p' 和 'div a' 的 query 规则,所以不触发打印。

MutationObserver 的异步批量回调机制确实需要比较细致的处理,最近开源的一个项目里也在一段[设计文档]( https://github.com/rrweb-io/rrweb/blob/master/docs/observer.zh_CN.md#%E6%96%B0%E5%A2%9E%E8%8A%82%E7%82%B9)里描述了一些这个机制导致的问题。
2018 年 7 月 13 日
回复了 firhome 创建的主题 程序员 这个前端录屏原理是怎么实现的?有大佬知道吗?
不确定 fundebug 是怎么做的,不过看原理应该和国外一个很成熟的产品 logrocket 中的回放功能类似。

首先这类功能一般是为了用户行为追踪或者捕获异常前的操作信息,以 sdk 的形式集成到用户的应用中。
因此侵入性强、计算消耗大、网络开销多的方案都是不可能产品化的,上面讨论的 canvas 方案基本也就不可能。

具体来说可以把这个过程看作是 DOM 快照链。

第一步:对界面 DOM 进行一次全量快照。这一步还包括样式的收集、js 脚本的去除等,并通过一定的规则给当前的每个 DOM 元素标记一个 id。

第二步:监听所有可能对界面产生影响的事件,例如各类鼠标事件、输入事件、滚动事件、缩放事件等等,每个事件都记录 payload 和 target,target 是某个 DOM 元素的话就记录为第一步中的 id,这样比记录一个 css selector 数据量要小不少,同时还会定时获取鼠标的绝对坐标,有变化时也记录为一个事件。这样的每一次变化事件可以记录为一次增量的快照。

第三步:一些优化处理,比如浏览器端存一定量的增量快照后再一起发送到服务端,减少网络开销;也包括多次增量之后再进行一次全量,对齐真实状态。

第四步:再后台观看“录制”的时候,其实就是先用全量快照渲染出界面,然后顺着快照链走,按照时间戳把对应的修改重放到页面上,就形成了像视频一样的回放。

以上是我看 logrocket 的视频并且试用,然后逆向了一小部分 sdk 代码之后的分析,仅供参考。
1  2  
关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   1929 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 15ms · UTC 15:46 · PVG 23:46 · LAX 07:46 · JFK 10:46
♥ Do have faith in what you're doing.