简介
目前想在一个纯 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 拷贝而来(有微调),如下:Plainenum 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](https://github.com/nodejs/node/issues/3916),需要把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结果如下
Discussion
留言与讨论
想法、补充和不同意见都欢迎。