来自 页面互动 |傀儡师:
定位器是选择元素并与其交互的推荐方式。定位器封装了如何选择元素的信息,它们允许 Puppeteer 自动等待元素出现在 DOM 中并处于执行操作的正确状态。您始终使用 page.locator() 或 frame.locator() 函数实例化定位器。如果定位器 API 没有提供您需要的功能,您仍然可以使用较低级别的 API,例如 page.waitForSelector() 或 ElementHandle。
这张表总结了我对
locator()
和$()
之间区别的理解:
方法 |
返校了 | 等待元素出现在 DOM 中? | 需要 吗? |
输入法 |
---|---|---|---|---|
|
|
是(重试直至成功或超时) | 没有 | 只有 、 、 、 |
|
|
没有 | 是的 | 各种 |
我有一些问题:
locator()
等待元素出现在 DOM 中,那么它不是必须有 await
吗?同样,如果$()
不等待,那不是就不需要await
吗?$()
这种不等待元素出现在 DOM 中的方法比等待元素出现的方法有更多的输入方法?locator()
是waitForSelector()
和$()
的组合吗?从根本上来说,对定位器可能存在一些误解。这是一个很好的入门读物:了解剧作家定位器承诺。
定位器和
page.$
(以及类似的调用)之间的主要区别是定位器是惰性的,并且在对它们调用操作之前不执行任何操作。调用 page.locator()
(还有 page.getByRole()
、page.getByText()
等——这些也是定位器)声明 一个选择策略,稍后可能会调用该策略。没有 await
,因为还没有任何操作,也没有异步 CDP(Chrome Devtools 协议)或浏览器自动化进程间调用。
另一方面,
page.$()
运行立即查询并返回元素句柄。
当你调用
await page.locator("p").click()
时,.click()
部分是一个连锁动作。考虑:
const elementHandle = await page.$("p");
await elementHandle.click();
使用定位器,您可以将其分成两行:
const locator = page.locator("p");
await locator.click();
在上面的示例中,这种差异可能看起来毫无意义,但是将选择与操作分开有许多微妙而重要的优点:
定位器默认使用严格模式,避免误报和静默失败。
定位器仅对浏览器进行一次 CDP 调用(而不是两次),并在操作的确切时刻查询元素,避免了在 CDP 调用之间页面发生更改的机会。
相反,
page.$
版本会进行两次调用,由于查询和操作是分离的,因此引发了竞争条件。如果元素无效或意外更改,可能会发生难以调试的故障。
定位器版本可以声明一次并轻松重用,声明和使用之间有很多“空间”(就代码和时间而言)。这有利于像POM这样的最佳实践测试方法,并使大型测试更容易维护。
定位器有许多最佳实践、面向用户的别名,例如
.getByRole()
,可以避免使用 CSS 或 XPath 查询页面等反模式。
因此,不鼓励和/或弃用
page.$
和在元素句柄上操作的类似函数。对于几乎所有的意图和目的,标准都是假装它们不存在。它们是从 Puppeteer 继承的遗留结构,但一旦 Playwright 团队创新了当前基于定位器的方法(Puppeteer 已经复制了自己的方法),它们就变得过时了。