返回专题

专题 2023

jsdom 与 worker-dom 学习记录

jsdom 与 worker-dom 学习以及改造成脱离 Node 的库

DOMJavaScriptW3C
本文目录12
  1. 简介
  2. 为什么不选 worker-dom?
  3. 原因
  4. 使用实例(Hello World)
  5. 宿主核心代码
  6. jsdom it is
  7. 实现原理
  8. Web IDL 示例详解(History)
  9. 快速开始
  10. 测试方式
  11. 运行 WPT 测试
  12. 在 worker 里运行 tests

简介

目前想在一个纯 JS 沙箱环境中使用 DOM 相关的操作,发现有 worker-domjsdom 两个库,其中:

  • worker-dom 是在 web-worker 内实现的 DOM API 标准,主要用于提升 JS 代码运行速度
  • jsdom 是纯 javascript 与 node 的实现的 DOM 与 HTML 标准,主要用于测试与抓包

为什么不选 worker-dom?

原因

  • 原因一: worker-dom 本质上还是使用的是浏览器的 DOM 标准,在 worker 中操作 DOM 并发送消息到宿主,由宿主代为执行,并没有从零开发一个 DOM 标准
  • 原因二:由于 worker 的通信是异步的订阅发布式,导致 DOM 的同步方法操作在 worker 中会变成异步操作,例如 getBoundingClientRect 方法

使用实例(Hello World)

  • index.html

  • hello-world.js

  • 运行效果

宿主核心代码

  • 在执行 MainThread.upgradeElement 的时候,会先请求代码字符串,然后执行 install 方法

  • install 方法会创建 workerContext 用于执行 worker 代码,并监听来自 worker 的消息

  • 其中消息格式是被简化过的,具体格式见调试日志

  • 宿主环境接收到消息之后通过统一的 mutatorContext 来对宿主环境的 DOM 进行操作

jsdom it is

实现原理

总的来说,Web 平台的类(像 WindowNodeLocationCSSStyleSheet)可以被 Web IDL 语言来描述,Web IDL 抽象并减少了很多样板代码,比如类型转换、参数校验、属性映射等。

综上所述,大多数 jsdom 里的 Web 类由以下两个文件实现

  • IDL 文件,比如 Attr.webidl ,或多或少直接从过饭中搬来
  • 实现文件,比如 Attr-impl.js,包括相关的实现逻辑

最后通过 yarn prepare 命令生成一个公共 API 文件(例如 Attr.js),里面包括了从 Web IDL 衍生出来的样板代码,引用到实现文件里的重要逻辑。然后把所有的功能通过 lib/jsdom/living/interfaces.js 文件暴露到 jsdom windows 里面。

Web IDL 示例详解(History)

  • History.webidl 文件从 WHATWG HTML Spec History Interface 拷贝而来(有微调),如下:

    Plain
    enum ScrollRestoration { "auto", "manual" };
    
    [Exposed=Window]
    interface History {
      readonly attribute unsigned long length;
      attribute ScrollRestoration scrollRestoration;
      readonly attribute any state;
      undefined go(optional long delta = 0);
      undefined back();
      undefined forward();
      undefined pushState(any data, DOMString unused, optional USVString? url = null);
      undefined replaceState(any data, DOMString unused, optional USVString? url = null);
    };
    
  • History-impl.js 实现核心的逻辑

  • 自动生成 History.js 文件

  • 其中 install 方法会被 interface.jsinstallInterface 方法引用到

  • installInterface 方法会在创建 window 的时候被执行

快速开始

  1. 下载代码并用 yarn 安装 NPM 包
  2. 执行构建命令 yarn prepare,该命令会执行以下逻辑
    1. 执行 ./scripts/webidl/convert.js 命令,生成 lib/jsdom/living/generated 目录与文件,改文件会被lib/jsdom/living/interfaces.js 引用到
    2. 执行 yarn generate-js-globals 命令,运行 vm 并通过 Object.getOwnPropertyDescriptors(this) 命令获取所有的全局变量属性,并写到 lib/jsdom/browser/js-globals.json 文件里面,后续会用于生成 _windowInstance_ 对象
  3. 初始化 web-platform-tests 源码与环境
    1. 初始化 git submodule,这一步有可能要挂代理,切等待时间较长
    2. 设置 python3 环境与 host ,详情见 Running Tests from the Local System
    3. 由于 wpt 的执行需要 Python3.6 或以上版本,而且 node child_process 的执行不支持 [bash alias 和 bash function](https://github.com/nodejs/node/issues/3916),需要把 start-wpt-serverrun-tuwpt.jspython 改为 python3
  4. 执行测试命令

测试方式

  1. web 平台功能测试 yarn test-wpt ,可以指定测试执行 yarn test-wpt --fgrep dom/events
  2. 自定义的 web 平台功能测试 yarn test-tuwpt,可以指定测试运行yarn test-tuwpt --fgrep domparsin
  3. 与 wpt 无关的 API 测试 yarn test-api,使用 mocha 合 chai 编写
  4. 在浏览器中直接运行 yarn test-browser-*,使用 karma 编写

运行 WPT 测试

  • 运行 yarn test-wpt --fgrep dom/events 结果

在 worker 里运行 tests

  • 刚开始看到 yarn test-browser-worker 十分开心,感觉 jsdom 一定能在 worker 里面运行,然而在看了 karma 配置后发现大意了,wpt 相关测试都不能在 iframe 或 worker 内运行

  • 不过至少可以确定打包后的代码在经过 browserify 转换后可以在 worker 与 iframe 中运行不报错

  • 运行 yarn test-browser-worker 结果如下

返回首页
上一篇啊鸡入坑 AI 之《老婆生成器(Stable Diffusion)》下一篇Chrome DevTools 调研

Discussion

留言与讨论

想法、补充和不同意见都欢迎。