前端开发者福音,一定要试试的 Web 远程调试工具!

GitHub 库房地址github.com/HuolalaTech…

前言

PageSpy 自开源供给服务以来,社区中收到很多用户反应说想了解 PageSpy 的一些工作原理,这么真诚而又火热的要求,咱们第一时间安排上了。本章就为咱们带来深化完结原理系列之「打印 window 方针,PageSpy 是怎么处理的?」。

Console 面板除了打印日志外,还支撑在 Console 面板底部发送调试代码到客户端,获取运行时的变量信息。

打印 window 方针,PageSpy 是怎么处理的?

还没有尝试过的小伙伴能够点击 在线体验,接下来将和咱们一同进入主题:PageSpy 怎么处理杂乱数据的交互

规划方针

PageSpy Console 面板最起初的规划方针便是向浏览器控制台的 Console 面板看齐。用户假如打印杂乱数据,单纯靠序列化源数据会面临以下问题:

  • 不支撑序列化的特点值会丢掉;
  • self-reference 的数据会报错;
  • Getter 特点值动态计算的交互特性会丢掉;
  • 无法沿着原型链查看数据明细;

上述说到的每一项在各自的场景中都发挥着无比重要的作用。所以能够明确的是:

  • 想要打印杂乱数据,肯定是需要先做什么处理后再运用;
  • 后续有查看特点明细的需求,所以需要将数据实体找个地方存起来;

什么是杂乱数据?

关于数据「杂乱不杂乱」是依照如下界说的:

  • 能够点击查看明细的数据都是杂乱数据,如 Object / Array / Set / Map 等类型的数据;
  • 一眼就看明白的都是不杂乱数据,如 "Hello" / 12345 / true / Symbol(foo) / Error / undefined 等;

在 PageSpy 中,咱们将对各种数据进行转化、缓存、查询、交互的逻辑封成 Atom 类(type: "atom" 代表杂乱数据),结构体如下所示:

// SpyAtom.Overview
interface Overview {
 id: string;
 type:
  | 'string'
  | 'number'
  | 'bigint'
  | 'boolean'
  | 'symbol'
  | 'undefined'
  | 'object'
  | 'function'
  | 'null'
  | 'error'
  | 'debug-origin'
  | 'atom';
 value: string | PropertyDescriptor;
 __atomId?: string;
 instanceId?: string;
}

正是这 20 行类型代码决定了本次主题中的详细完结:打印 window 方针便是依赖这套接口类型界说。

为什么要强调打印的是 window 方针?因为 window 方针具有上述一切的 “问题” 于一身:

  • 有不支撑序列化的特点数据,比如 window.alert() 这种大局方法的特点;
  • 有很多 Getter 特点,如 outerWidth / innerWidth / location 等;
  • self-reference,如 window.self
  • 能够沿着原型链查看数据明细;

怎么完结

咱们在上面说到了:「用户假如打印杂乱数据,单纯靠序列化源数据会面临……」,那打印不杂乱数据还会有那么多问题吗?答案是不会,关于这些不杂乱的数据咱们只需要拿到值,并获取到它的类型即可完结烘托。

明确了数据复不杂乱之后,咱们从以上类型界说中把 type: "atom" 杂乱数据类型的界说独自拎出来:

// SpyAtom.Overview
interface Overview {
 id: string;
 type: 'atom';
 value: string | PropertyDescriptor;
 __atomId: string;
 instanceId: string;
}
  • id:作为数据的仅有标识。
  • __atomId:打印杂乱数据时,数据实体会被记录在 Atom 的 storeMap 中,__atomId 在 storeMap 中作为 key,对应一个方针源数据。
  • instanceId:实质的值是由 __atomId 衍生。默许和 __atomId 持平、指向当时打印出来的方针;点击方针打开,每个内层特点都有一个 instanceId 指向父级(上一层)的方针;

除了以上三个 id 之外,咱们还用到了一个被称为 parentId 的数据:

  • parentId:实质的值也是由 __atomId 衍生。用于获取 Getter 特点值,详细的获取行为原理是 Object.getOwnPropertyDescriptor(store[parentId], key).get.call(store[instanceId])

总结

经过以上的内容介绍,咱们再来回顾下打印 window 方针会遇到的问题,现在都有了相对应的答案:

怎么处理不支撑序列化的特点数据,比如函数?

答:第一步先经过获取类型将数据以「杂乱」维度区分,结合数据值来描绘一个数据该怎么显现;而函数归于不杂乱数据,在确定是函数类型后、经过 fn.toString() 获取值即可在调试端烘托。

Getter 特点动态计算特点值的交互特性会丢掉?

答:Getter 特点值的获取实质上是调用函数,函数体内会运用 this 指针。那么根据以上杂乱数据结构的界说,咱们有 parentId / key / instanceId,根据 parentId 和 key 能够获取 getter 函数自身,再经过 instanceId 知道了实例方针,那么接下来组合调用一下就能完美解决:Object.getOwnPropertyDescriptor(store[parentId], key).get.call(store[instanceId])

怎么处理 self-reference 方针无法序列化的问题,如 window.self

答:PageSpy 只会转化当时方针层级下的不杂乱的数据特点。打开 window 时,PageSpy 会拿到 self 特点,但判别出它是一个杂乱数据,SDK 不会直接进行计算、转化处理,而是告知调试端 window.self 的描绘符具有 getter 特点,等待调试端下次来查询时,再进值计算并返回值

怎么沿着原型链查看数据明细?

答:虽然经过 Object.getOwnPropertyDescriptors(someObj) 返回的 descriptors 描绘符不会给咱们原型方针的数据,可是咱们能够手动增加一个 [[Prototype]] 加上去就能够满足用户沿着原型链点击查看数据明细啦!

以上便是本次深化 PageSpy 完结原理系列文章的全部内容了,欢迎咱们在工作中集成运用,遇到问题能够在 Github 反应或许参加技术支撑群(中文 README 有群二维码),咱们将第一时间解答。