jsdom 与 worker-dom 学习记录
Last Edited Time
Jun 10, 2023 03:56 AM
date
Jan 9, 2023
slug
jsdom-and-worker-dom
status
Published
tags
W3C
JavaScript
DOM
summary
jsdom 与 worker-dom 学习以及改造成脱离 Node 的库
type
Post
简介为什么不选 worker-dom?原因使用实例(Hello World)宿主核心代码jsdom it is实现原理Web IDL 示例详解(History)快速开始测试方式运行 WPT 测试在 worker 里运行 tests Reference
简介
目前想在一个纯 JS 沙箱环境中使用 DOM 相关的操作,发现有 worker-dom 与 jsdom 两个库,其中:
- 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 平台的类(像
Window
、Node
、Location
或 CSSStyleSheet
)可以被 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 拷贝而来(有微调),如下:
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.js
的installInterface
方法引用到
installInterface
方法会在创建window
的时候被执行
快速开始
- 下载代码并用
yarn
安装 NPM 包
- 执行构建命令
yarn prepare
,该命令会执行以下逻辑 - 执行
./scripts/webidl/convert.js
命令,生成lib/jsdom/living/generated
目录与文件,改文件会被lib/jsdom/living/interfaces.js
引用到 - 执行
yarn generate-js-globals
命令,运行vm
并通过Object.getOwnPropertyDescriptors(this)
命令获取所有的全局变量属性,并写到lib/jsdom/browser/js-globals.json
文件里面,后续会用于生成windowInstance
对象
- 初始化
web-platform-tests
源码与环境 - 初始化 git submodule,这一步有可能要挂代理,切等待时间较长
- 设置
python3
环境与host
,详情见 Running Tests from the Local System - 由于 wpt 的执行需要 Python3.6 或以上版本,而且
node child_process
的执行不支持bash alias 和 bash function
,需要把start-wpt-server
与run-tuwpt.js
的python
改为python3
- 执行测试命令
测试方式
- web 平台功能测试
yarn test-wpt
,可以指定测试执行yarn test-wpt --fgrep dom/events
- 自定义的 web 平台功能测试
yarn test-tuwpt
,可以指定测试运行yarn test-tuwpt --fgrep domparsin
- 与 wpt 无关的 API 测试
yarn test-api
,使用 mocha 合 chai 编写
- 在浏览器中直接运行
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
结果如下