Web前端开发网

fly63.com

首页 资源 工具 文章 教程 栏目
  • 在线搜索
  • 文章标签
  • 广告合作
  • 网站投稿
  • 赞助一下
  • 关于我们
搜索

在线工具_工作生活好帮手

打造各种简单、易用、便捷的在线工具,网友无需注册和下载安装即可使用

点击查看

资源分类

AI智能 酷站推荐 招聘/兼职 框架/库 模块/管理 移动端UI框架 Web-UI框架 Js插件 Jquery插件 CSS相关 IDE环境 在线工具 图形动效 游戏框架 node相关 调试/测试 在线学习 社区/论坛 博客/团队 前端素材 图标/图库 建站资源 设计/灵感 IT资讯
提交资源 / 链接反馈

jsdom

分享
复制链接
新浪微博
QQ 好友

扫一扫分享

网站地址:https://github.com/jsdom/jsdom
GitHub:https://github.com/jsdom/jsdom
网站描述:用于在node中解析html
访问官网 GitHub
jsdom和cheerio 类似,用来在node上解析html。但是它还有加载页面的功能。


什么是jsdom?

jsdom是一个纯JavaScript实现的Web标准库,完全遵循WHATWG DOM和HTML标准规范。它能够在Node.js环境中模拟浏览器的DOM环境,让你可以:

  • 解析和操作HTML文档
  • 执行JavaScript脚本
  • 进行Web应用测试
  • 实现网页抓取和数据提取


基本用法

const jsdom = require("jsdom");
const { JSDOM } = jsdom;

要使用 jsdom,你将主要使用 JSDOM 构造函数,它是 jsdom 主模块的命名导出。将一个字符串传递给构造函数。你将返回一个 JSDOM 对象,该对象具有许多有用的属性,特别是 window:

const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
console.log(dom.window.document.querySelector("p").textContent); // "Hello world"

(请注意,jsdom 将像浏览器一样解析你传递给它的 HTML,包括隐含的 <html>、<head> 和 <body> 标签。)

生成的对象是 JSDOM 类的一个实例,除了 window 之外,它还包含许多有用的属性和方法。通常,它可用于对来自 "外部," 的 jsdom 执行操作,从而执行使用普通 DOM api 无法完成的操作。对于简单情况,你不需要任何此功能,我们建议使用如下编码模式

const { window } = new JSDOM(`...`);
// or even
const { document } = (new JSDOM(`...`)).window;

有关你可以使用 JSDOM 类执行的所有操作的完整文档位于下面的“JSDOM 对象 API”部分。


自定义 jsdom

JSDOM 构造函数接受第二个参数,该参数可用于通过以下方式自定义 jsdom。

简单选项

const dom = new JSDOM(``, {
  url: "https://example.org/",
  referrer: "https://example.com/",
  contentType: "text/html",
  includeNodeLocations: true,
  storageQuota: 10000000
});
  • url 设置 window.location、document.URL 和 document.documentURI 返回的值,并影响文档内相对 URL 的解析以及获取子资源时使用的同源限制和引用站点等内容。它默认为 "about:blank"。

  • referrer 仅影响从 document.referrer 读取的值。它默认为无引用者(反映为空字符串)。

  • contentType 影响从 document.contentType 读取的值,以及文档的解析方式:作为 HTML 或 XML。不是 HTML MIME 类型 或 XML MIME 类型 的值将被抛出。它默认为 "text/html"。如果存在 charset 参数,它会影响 二进制数据处理。

  • includeNodeLocations 保留 HTML 解析器生成的位置信息,允许你使用 nodeLocation() 方法(如下所述)检索它。它还确保在 <script> 元素内运行的代码的异常堆栈跟踪中报告的行号是正确的。它默认为 false 以获得最佳性能,并且不能与 XML 内容类型一起使用,因为我们的 XML 解析器不支持位置信息。

  • storageQuota 是 localStorage 和 sessionStorage 使用的单独存储区域的最大代码单元大小。尝试存储大于此限制的数据将导致抛出 DOMException。默认情况下,它设置为每个来源 5,000,000 个代码单元,这是受 HTML 规范的启发。

请注意,url 和 referrer 在使用前都已规范化,因此例如如果你传入 "https:example.com",jsdom 会将其解释为你已传入 "https://example.com/"。如果你传递了无法解析的 URL,则调用将抛出。(URL 根据 URL 标准 进行解析和序列化。)

执行脚本

jsdom 最强大的功能是它可以在 jsdom 内执行脚本。这些脚本可以修改页面的内容并访问 jsdom 实现的所有 Web 平台 API。

但是,在处理不受信任的内容时,这也非常危险。jsdom 沙箱并非万无一失,如果足够努力,在 DOM 的 <script> 中运行的代码可以访问 Node.js 环境,从而访问你的机器。因此,默认情况下禁用执行 HTML 中嵌入的脚本的功能:

const dom = new JSDOM(`<body>
  <div id="content"></div>
  <script>document.getElementById("content").append(document.createElement("hr"));</script>
</body>`);

// The script will not be executed, by default:
console.log(dom.window.document.getElementById("content").children.length); // 0

要启用页面内的执行脚本,可以使用 runScripts: "dangerously" 选项:

const dom = new JSDOM(`<body>
  <div id="content"></div>
  <script>document.getElementById("content").append(document.createElement("hr"));</script>
</body>`, { runScripts: "dangerously" });

// The script will be executed and modify the DOM:
console.log(dom.window.document.getElementById("content").children.length); // 1

我们再次强调,仅在提供你知道安全的 jsdom 代码时才使用它。如果你将它用于任意用户提供的代码或来自互联网的代码,那么你实际上是在运行不受信任的 Node.js 代码,并且你的机器可能会受到威胁。

如果你想执行通过 <script src=""> 包含的外部脚本,你还需要确保它们加载它们。为此,请添加选项 resources: "usable" 如下所述。(你可能还想设置 url 选项,原因如上所述在那里。)

事件处理程序属性(如 <div onclick="">)也受此设置控制;除非将 runScripts 设置为 "dangerously",否则它们将不起作用。(但是,事件处理程序属性(如 div.onclick = ...)无论 runScripts 如何都将起作用。)

如果你只是想执行脚本 "从外部",而不是让 <script> 元素和事件处理程序属性运行 "从内部",则可以使用 runScripts: "outside-only" 选项,该选项允许在 window 上安装所有 JavaScript 规范提供的全局变量的新副本。这包括 window.Array、window.Promise 等。值得注意的是,它还包括 window.eval,它允许运行脚本,但使用 jsdom window 作为全局变量:

const dom = new JSDOM(`<body>
  <div id="content"></div>
  <script>document.getElementById("content").append(document.createElement("hr"));</script>
</body>`, { runScripts: "outside-only" });

// run a script outside of JSDOM:
dom.window.eval('document.getElementById("content").append(document.createElement("p"));');

console.log(dom.window.document.getElementById("content").children.length); // 1
console.log(dom.window.document.getElementsByTagName("hr").length); // 0
console.log(dom.window.document.getElementsByTagName("p").length); // 1

出于性能原因,此功能默认关闭,但可以安全启用。

请注意,在默认配置中,如果不设置 runScripts,window.Array、window.eval 等的值将与外部 Node.js 环境提供的值相同。也就是说,window.eval === eval 将保持不变,因此 window.eval 将无法以有用的方式运行脚本。

我们强烈建议不要尝试通过将 jsdom 和 Node 全局环境混合在一起(例如通过执行 global.window = dom.window)来实现 "执行脚本",然后在 Node 全局环境中执行脚本或测试代码。相反,你应该像对待浏览器一样对待 jsdom,并使用 window.eval 或 runScripts: "dangerously" 运行所有需要访问 jsdom 环境中的 DOM 的脚本和测试。例如,这可能需要创建一个 browserify 包以作为 <script> 元素执行 - 就像在浏览器中一样。

最后,对于高级用例,你可以使用下面记录的 dom.getInternalVMContext() 方法。

假装是可视化浏览器

jsdom 没有渲染视觉内容的能力,默认情况下会像无头浏览器一样运行。它通过 document.hidden 等 API 向网页提供提示,提示其内容不可见。

当 pretendToBeVisual 选项设置为 true 时,jsdom 将假装它正在渲染和显示内容。它通过以下方式实现:

  • 将 document.hidden 更改为返回 false 而不是 true

  • 将 document.visibilityState 更改为返回 "visible" 而不是 "prerender"

  • 启用 window.requestAnimationFrame() 和 window.cancelAnimationFrame() 方法,否则不存在

const window = (new JSDOM(``, { pretendToBeVisual: true })).window;

window.requestAnimationFrame(timestamp => {
  console.log(timestamp > 0);
});

请注意,jsdom 仍然是 不进行任何布局或渲染,所以这实际上只是假装是可视化的,而不是实现真正的可视化 Web 浏览器将实现的平台部分。

加载子资源

基本选项

默认情况下,jsdom 不会加载任何子资源,例如脚本、样式表、图片或 iframe。如果你希望 jsdom 加载此类资源,你可以传递 resources: "usable" 选项,它将加载所有可用资源。那些是:

  • 通过 <frame> 和 <iframe> 的框架和 iframe

  • 样式表,通过 <link rel="stylesheet">

  • 脚本,通过 <script>,但前提是 runScripts: "dangerously" 也已设置

  • 图片,通过 <img>,但前提是还安装了 canvas npm 包(请参阅下面的“Canvas 支持”)

尝试加载资源时,请记住 url 选项的默认值是 "about:blank",这意味着通过相对 URL 包含的任何资源都将无法加载。(尝试根据 URL about:blank 解析 URL /something 的结果是一个错误。)因此,在这些情况下,你可能希望为 url 选项设置非默认值,或者使用自动执行此操作的 便捷 API 之一。

高级配置

为了更全面地定制 jsdom 的资源加载行为,你可以传递 ResourceLoader 类的实例作为 resources 选项值:

const resourceLoader = new jsdom.ResourceLoader({
  proxy: "http://127.0.0.1:9001",
  strictSSL: false,
  userAgent: "Mellblomenator/9000",
});
const dom = new JSDOM(``, { resources: resourceLoader });

ResourceLoader 构造函数的三个选项是:

  • proxy 是要使用的 HTTP 代理的地址。

  • strictSSL 可以设置为 false 以禁用 SSL 证书有效的要求。

  • userAgent 影响发送的 User-Agent 标头,从而影响 navigator.userAgent 的结果值。它默认为 Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}。

你可以通过子类化 ResourceLoader 并重写 fetch() 方法来进一步自定义资源获取。例如,这是一个覆盖为特定 URL 提供的响应的版本:

class CustomResourceLoader extends jsdom.ResourceLoader {
  fetch(url, options) {
    // Override the contents of this script to do something unusual.
    if (url === "https://example.com/some-specific-script.js") {
      return Promise.resolve(Buffer.from("window.someGlobal = 5;"));
    }

    return super.fetch(url, options);
  }
}

jsdom 将在遇到 "usable" 资源时调用自定义资源加载器的 fetch() 方法,如上节所述。该方法采用 URL 字符串以及一些选项,如果调用 super.fetch(),你应该不加修改地传递这些选项。它必须返回 Node.js Buffer 对象的 promise,或者如果资源有意不加载,则返回 null。通常,大多数情况下都希望委托给 super.fetch(),如下所示。

你将在 fetch() 中收到的选项之一是获取资源的元素(如果适用)。

class CustomResourceLoader extends jsdom.ResourceLoader {
  fetch(url, options) {
    if (options.element) {
      console.log(`Element ${options.element.localName} is requesting the url ${url}`);
    }

    return super.fetch(url, options);
  }
}

虚拟控制台

与 Web 浏览器一样,jsdom 具有 "console" 的概念。这记录了直接从页面发送的信息(通过在文档内执行的脚本)以及来自 jsdom 实现本身的信息。我们将用户可控制的控制台称为 "虚拟控制台",以将其与 Node.js console API 和页面内的 window.console API 区分开来。

默认情况下,JSDOM 构造函数将返回一个带有虚拟控制台的实例,该控制台将其所有输出转发到 Node.js 控制台。要创建自己的虚拟控制台并将其传递给 jsdom,你可以通过执行以下操作覆盖此默认值

const virtualConsole = new jsdom.VirtualConsole();
const dom = new JSDOM(``, { virtualConsole });

这样的代码将创建一个没有行为的虚拟控制台。你可以通过为所有可能的控制台方法添加事件监听器来赋予它行为:

virtualConsole.on("error", () => { ... });
virtualConsole.on("warn", () => { ... });
virtualConsole.on("info", () => { ... });
virtualConsole.on("dir", () => { ... });
// ... etc. See https://console.spec.whatwg.org/#logging

(请注意,最好在调用 new JSDOM() 之前设置这些事件监听器,因为解析期间可能会出现错误或控制台调用脚本。)

如果你只是想将虚拟控制台输出重定向到另一个控制台,例如默认的 Node.js 控制台,你可以这样做

virtualConsole.sendTo(console);

还有一个特殊事件 "jsdomError",它将使用错误对象触发以报告来自 jsdom 本身的错误。这类似于错误消息经常出现在 Web 浏览器控制台中的方式,即使它们不是由 console.error 发起的。到目前为止,以下错误以这种方式输出:

  • 加载或解析子资源(脚本、样式表、框架和 iframe)时出错

  • 脚本执行错误,这些错误未由返回 true 或调用 event.preventDefault() 的窗口 onerror 事件处理程序处理

  • 由于调用方法(如 window.alert)而导致未实现的错误,jsdom 未实现这些方法,但为了实现 Web 兼容性还是会安装这些方法

如果你使用 sendTo(c) 将错误发送到 c,默认情况下它将使用来自 "jsdomError" 事件的信息调用 c.error(errorStack[, errorDetail])。如果你希望保持事件与方法调用的严格一对一映射,并且可能自己处理 "jsdomError",那么你可以这样做

virtualConsole.sendTo(c, { omitJSDOMErrors: true });

Cookie jar

与 Web 浏览器一样,jsdom 具有 cookie jar 的概念,用于存储 HTTP cookie。具有与文档位于同一域上的 URL 且未标记为仅 HTTP 的 Cookie 可通过 document.cookie API 访问。此外,cookie jar 中的所有 cookie 都会影响子资源的获取。

默认情况下,JSDOM 构造函数将返回一个带有空 cookie jar 的实例。要创建自己的 cookie jar 并将其传递给 jsdom,你可以通过执行以下操作覆盖此默认值

const cookieJar = new jsdom.CookieJar(store, options);
const dom = new JSDOM(``, { cookieJar });

如果你想在多个 jsdom 之间共享同一个 cookie jar,或者提前用某些值填充 cookie jar,这非常有用。

Cookie jar 由 tough-cookie 包提供。jsdom.CookieJar 构造函数是 Tough-cookie cookie jar 的子类,默认情况下会设置 looseMode: true 选项,因为 与浏览器的行为更匹配。如果你想自己使用 strong-cookie 的实用程序和类,你可以使用 jsdom.toughCookie 模块导出来访问使用 jsdom 打包的 strong-cookie 模块实例。

解析前干预

jsdom 允许你很早就干预 jsdom 的创建:在创建 Window 和 Document 对象之后,但在解析任何 HTML 以使用节点填充文档之前:

const dom = new JSDOM(`<p>Hello</p>`, {
  beforeParse(window) {
    window.document.childNodes.length === 0;
    window.someCoolAPI = () => { /* ... */ };
  }
});

如果你想以某种方式修改环境,这尤其有用,例如为 jsdom 不支持的 Web 平台 API 添加垫片。


JSDOM 对象 API

一旦你构造了一个 JSDOM 对象,它将具有以下有用的功能:

属性

属性 window 检索为你创建的 Window 对象。

属性 virtualConsole 和 cookieJar 反映了你传入的选项,或者如果没有传入这些选项,则反映了为你创建的默认值。

使用 serialize() 序列化文档

serialize() 方法将返回文档的 HTML 序列化,包括 doctype:

const dom = new JSDOM(`<!DOCTYPE html>hello`);

dom.serialize() === "<!DOCTYPE html><html><head></head><body>hello</body></html>";

// Contrast with:
dom.window.document.documentElement.outerHTML === "<html><head></head><body>hello</body></html>";

使用 nodeLocation(node) 获取节点的源位置

nodeLocation() 方法将查找 DOM 节点在源文档中的位置,并返回该节点的 parse5 位置信息:

const dom = new JSDOM(
  `<p>Hello
    <img src="foo.jpg">
  </p>`,
  { includeNodeLocations: true }
);

const document = dom.window.document;
const bodyEl = document.body; // implicitly created
const pEl = document.querySelector("p");
const textNode = pEl.firstChild;
const imgEl = document.querySelector("img");

console.log(dom.nodeLocation(bodyEl));   // null; it's not in the source
console.log(dom.nodeLocation(pEl));      // { startOffset: 0, endOffset: 39, startTag: ..., endTag: ... }
console.log(dom.nodeLocation(textNode)); // { startOffset: 3, endOffset: 13 }
console.log(dom.nodeLocation(imgEl));    // { startOffset: 13, endOffset: 32 }

请注意,此功能仅在你设置了 includeNodeLocations 选项时才有效;出于性能原因,节点位置默认关闭。

使用 getInternalVMContext() 与 Node.js vm 模块交互

Node.js 的内置 vm 模块是 jsdom 脚本运行魔法的基础。一些高级用例,例如预编译脚本然后多次运行它,可以从直接将 vm 模块与 jsdom 创建的 Window 一起使用中受益。

要访问适合与 vm API 一起使用的 上下文全局对象,可以使用 getInternalVMContext() 方法:

const { Script } = require("vm");

const dom = new JSDOM(``, { runScripts: "outside-only" });
const script = new Script(`
  if (!this.ran) {
    this.ran = 0;
  }

  ++this.ran;
`);

const vmContext = dom.getInternalVMContext();

script.runInContext(vmContext);
script.runInContext(vmContext);
script.runInContext(vmContext);

console.assert(dom.window.ran === 3);

这是相当高级的功能,除非你有非常特殊的需求,否则我们建议坚持使用普通的 DOM API(例如 window.eval() 或 document.createElement("script"))。

请注意,如果创建 JSDOM 实例时未设置 runScripts,或者你是 在 Web 中使用 jsdom 浏览器,则此方法将引发异常。

使用重新配置 jsdom reconfigure(settings)

window 上的 top 属性在规范中标记为 [Unforgeable],这意味着它是一个不可配置的自有属性,因此即使使用 Object.defineProperty,也无法被 jsdom 中运行的正常代码覆盖或遮蔽。

同样,目前 jsdom 不处理导航(例如设置 window.location.href = "https://example.com/");这样做会导致虚拟控制台触发 "jsdomError",解释此功能未实现,并且不会发生任何变化:不会有新的 Window 或 Document 对象,现有 window 的 location 对象仍将具有所有相同的属性值。

但是,如果你从窗口外部操作,例如在创建 jsdom 的某个测试框架中,你可以使用特殊的 reconfigure() 方法覆盖其中一个或两个:

const dom = new JSDOM();

dom.window.top === dom.window;
dom.window.location.href === "about:blank";

dom.reconfigure({ windowTop: myFakeTopForTesting, url: "https://example.com/" });

dom.window.top === myFakeTopForTesting;
dom.window.location.href === "https://example.com/";

请注意,更改 jsdom 的 URL 将影响所有返回当前文档 URL 的 API,例如 window.location、document.URL 和 document.documentURI,以及文档内相对 URL 的解析,以及获取子资源时使用的同源检查和引用者。但是,它不会执行到该 URL 内容的导航;DOM 的内容将保持不变,并且不会创建 Window、Document 等的新实例。


便利 API

fromURL()

除了 JSDOM 构造函数本身之外,jsdom 还提供了一个返回 promise 的工厂方法,用于从 URL 构造 jsdom:

JSDOM.fromURL("https://example.com/", options).then(dom => {
  console.log(dom.serialize());
});

如果 URL 有效且请求成功,则返回的 promise 将通过 JSDOM 实例实现。任何重定向都将跟踪到其最终目的地。

提供给 fromURL() 的选项与提供给 JSDOM 构造函数的选项类似,但具有以下附加限制和后果:

  • 无法提供 url 和 contentType 选项。

  • referrer 选项用作初始请求的 HTTP Referer 请求标头。

  • resources 选项还会影响初始请求;如果你想要配置代理(请参见上文),这很有用。

  • 生成的 jsdom 的 URL、内容类型和引用者由响应确定。

  • 通过 HTTP Set-Cookie 响应标头设置的任何 cookie 都存储在 jsdom 的 cookie jar 中。同样,任何已在提供的 cookie jar 中的 cookie 都会作为 HTTP Cookie 请求标头发送。

fromFile()

与 fromURL() 类似,jsdom 还提供了一个 fromFile() 工厂方法,用于从文件名构造 jsdom:

JSDOM.fromFile("stuff.html", options).then(dom => {
  console.log(dom.serialize());
});

如果可以打开给定的文件,则返回的 promise 将通过 JSDOM 实例实现。与 Node.js API 中一样,文件名是相对于当前工作目录给出的。

提供给 fromFile() 的选项与提供给 JSDOM 构造函数的选项类似,但具有以下附加默认值:

  • url 选项将默认为与给定文件名相对应的文件 URL,而不是 "about:blank"。

  • 如果给定的文件名以 .xht、.xhtml 或 .xml 结尾,则 contentType 选项将默认为 "application/xhtml+xml";否则它将继续默认为 "text/html"。

fragment()

对于最简单的情况,你可能不需要整个 JSDOM 实例及其所有相关功能。你甚至可能不需要 Window 或 Document!相反,你只需要解析一些 HTML,并获取可以操作的 DOM 对象。为此,我们有 fragment(),它从给定字符串创建 DocumentFragment:

const frag = JSDOM.fragment(`<p>Hello</p><p><strong>Hi!</strong>`);

frag.childNodes.length === 2;
frag.querySelector("strong").textContent === "Hi!";
// etc.

这里 frag 是一个 DocumentFragment 实例,其内容是通过解析提供的字符串创建的。解析是使用 <template> 元素完成的,因此你可以在其中包含任何元素(包括具有奇怪解析规则的元素,如 <td>)。同样重要的是要注意,生成的 DocumentFragment 将没有 关联的浏览上下文:也就是说,元素的 ownerDocument 将具有空的 defaultView 属性,资源将不会加载等。

所有对 fragment() 工厂的调用都会导致 DocumentFragment 共享相同的模板所有者 Document。这允许多次调用 fragment(),而无需额外开销。但这也意味着对 fragment() 的调用无法使用任何选项进行自定义。

请注意,使用 DocumentFragment 进行序列化并不像使用完整的 JSDOM 对象那样容易。如果你需要序列化 DOM,你可能应该更直接地使用 JSDOM 构造函数。但对于包含单个元素的片段的特殊情况,通过正常方式很容易做到:

const frag = JSDOM.fragment(`<p>Hello</p>`);
console.log(frag.firstChild.outerHTML); // logs "<p>Hello</p>"


其他值得注意的功能

Canvas 支持

jsdom 支持使用 canvas 包通过画布 API 扩展任何 <canvas> 元素。要使其工作,你需要将 canvas 作为依赖包含在项目中,作为 jsdom 的对等项。如果 jsdom 可以找到 canvas 包的 3.x 版本,它将使用它,但如果不存在,则 <canvas> 元素的行为将类似于 <div>。

编码嗅探

除了提供字符串之外,还可以为 JSDOM 构造函数提供二进制数据,形式为 Node.js Buffer 或标准 JavaScript 二进制数据类型,如 ArrayBuffer、Uint8Array、DataView 等。完成后,jsdom 将从提供的字节中进行 嗅探编码,就像浏览器一样扫描 <meta charset> 标签。

如果提供的 contentType 选项包含 charset 参数,则该编码将覆盖嗅探的编码 - 除非存在 UTF-8 或 UTF-16 BOM,在这种情况下这些优先。(再次强调,这就像浏览器一样。)

此编码嗅探也适用于 JSDOM.fromFile() 和 JSDOM.fromURL()。在后一种情况下,与响应一起发送的任何 Content-Type 标头都将优先,方式与构造函数的 contentType 选项相同。

请注意,在许多情况下,以这种方式提供字节可能比提供字符串更好。例如,如果你尝试使用 Node.js 的 buffer.toString("utf-8") API,Node.js 将不会删除任何前导 BOM。如果你将此字符串提供给 jsdom,它将逐字解释它,使 BOM 保持完整。但 jsdom 的二进制数据解码代码将像浏览器一样删除前导 BOM;在这种情况下,直接提供 buffer 将产生所需的结果。

关闭 jsdom

根据定义,jsdom 中的计时器(由 window.setTimeout() 或 window.setInterval() 设置)将在窗口上下文中执行未来的代码。由于以后无法在不保持进程活动的情况下执行代码,因此出色的 jsdom 计时器将使你的 Node.js 进程保持活动状态。同样,由于无法在不保持该对象处于活动状态的情况下在对象的上下文中执行代码,因此未完成的 jsdom 计时器将阻止对它们所安排的窗口进行垃圾收集。

如果你想确保关闭 jsdom 窗口,请使用 window.close(),它将终止所有正在运行的计时器(并且还会删除窗口和文档上的任何事件监听器)。

使用 Chrome DevTools 调试 DOM

在 Node.js 中,你可以使用 Chrome DevTools 调试程序。请参阅 官方文档 了解如何开始。

默认情况下,jsdom 元素在控制台中被格式化为普通的旧 JS 对象。为了更轻松地调试,可以使用 jsdom-devtools-formatter,它可以让你像检查真实 DOM 元素一样检查它们。


注意事项

异步脚本加载

人们在使用 jsdom 时经常遇到异步脚本加载问题。许多页面异步加载脚本,但无法判断它们何时完成加载,因此无法判断何时是运行代码并检查生成的 DOM 结构的好时机。这是一个基本限制;我们无法预测网页上的脚本会做什么,因此无法告诉你它们何时完成更多脚本的加载。

这可以通过几种方式解决。如果你控制相关页面,最好的方法是使用脚本加载器提供的任何机制来检测加载何时完成。例如,如果你使用像 RequireJS 这样的模块加载器,代码可能如下所示:

// On the Node.js side:
const window = (new JSDOM(...)).window;
window.onModulesLoaded = () => {
  console.log("ready to roll!");
};
<!-- Inside the HTML you supply to jsdom -->
<script>
requirejs(["entry-module"], () => {
  window.onModulesLoaded();
});
</script>

如果你无法控制页面,你可以尝试变通方法,例如轮询特定元素是否存在。

有关更多详细信息,请参阅 #640 中的讨论,尤其是 @matthewkastor 的 有见地的评论。

Web 平台未实现的部分

虽然我们喜欢向 jsdom 添加新功能并使其与最新的 Web 规范保持同步,但它有许多缺失的 API。如果有任何缺失,请随时提交问题,但我们的团队很小而且很忙,因此拉取请求可能会更好。

jsdom 的某些功能由我们的依赖提供。在这方面值得注意的文档包括我们的 css 选择器引擎 nwsapi 的 支持的 CSS 选择器 列表。

除了我们尚未涉及的功能之外,还有两个主要功能目前不在 jsdom 的范围内。这些是:

  • 导航:单击链接或分配 location.href 或类似内容时,能够更改全局对象和所有其他对象。

  • 布局:能够计算元素在 CSS 中的视觉布局,这会影响 getBoundingClientRects() 等方法或 offsetTop 等属性。

目前,jsdom 对这些功能的某些方面具有虚拟行为,例如将 "未实现" "jsdomError" 发送到虚拟控制台进行导航,或为许多与布局相关的属性返回零。通常你可以在代码中解决这些限制,例如通过在抓取过程中为每个页面创建新的 JSDOM 实例,或使用 Object.defineProperty() 更改各种与布局相关的 getter 和方法的返回内容。

仅供个人学习参考/导航指引使用,具体请以第三方网站说明为准,本站不提供任何专业建议。如果地址失效或描述有误,请联系站长反馈~感谢您的理解与支持!

链接: https://fly63.com/nav/763

more>>
相关栏目
Express
基于 Node.js 平台,快速、开放、极简的 web 开发框架
官网 GitHub
pm2
pm2 是一个带有负载均衡功能的Node应用的进程管理器
官网 GitHub
Koa
基于 Node.js 平台的下一代 web 开发框架
官网 GitHub
Egg.js
为企业级框架和应用而生
官网 GitHub
Primus
通用包装器实时框架
点击进入 GitHub
Electrode
一个用于构建通用 React / Node.js 应用程序的平台
点击进入 GitHub
faye-websocket
Node.js 应用程序中命令预先存在的 WebSocket 连接
点击进入 GitHub
vm2
一个Node.js 官方 vm 库的替代品
点击进入 GitHub
node-opencv
node视觉算法库
官网 GitHub
node-xml2js
Json与xml相互转化的工具
点击进入 GitHub
node-red
一套开源可视化界面开发工具
官网 GitHub
polemo
网易开源的游戏后端框架
点击进入 GitHub
AdonisJs
一款类似laravel的node.js框架
官网 GitHub
GitBook
基于 Node.js 的命令行工具,可使用 Github/Git 和 Markdown 来制作精美的电子书。
官网
nodebestpractices
NodeJS最佳实践
官网 GitHub
Inquirer.js
NodeJs交互式命令行工具
点击进入 GitHub

手机扫一扫

》
分享组件加载中...
首页 技术导航 在线工具 技术文章 教程资源 前端标签 AI工具集 前端库/框架 实用工具箱 广告合作 关于我们

Copyright © 2018 Web前端开发网提供免费在线工具、编程学习资源(教程/框架/库),内容以学习参考为主,助您解决各类实际问题,快速提升专业能力。