向 puppeteer.Page 对象添加自定义方法

问题描述 投票:0回答:3

我想向

puppeteer.Page
对象添加自定义方法,这样我就可以像这样调用它们:

let page = await browser.newPage();
page.myNewCustomMethod();

这是我创建的众多自定义方法之一。它使用表达式数组通过 XPath 表达式查找第一个可用元素:

const findAnyByXPath = async function (page: puppeteer.Page, expressions: string[]) {
    for (const exp of expressions) {
        const elements = await page.$x(exp);

        if (elements.length) {
            return elements[0];
        }
    }

    return null;
}

我必须像这样调用它......

let element = await findAnyByXPath(page, arrayOfExpressions);

对我来说,这在编辑器中看起来很奇怪,尤其是在调用许多自定义方法的区域。在我看来,有点“断章取义”。所以我宁愿这样调用它:

page.findAnyByXPath(arrayOfExpressions);

我知道有一个

page.exposeFunction
方法,但这不是我要找的。

有什么方法可以实现这一目标?

javascript typescript methods puppeteer extending
3个回答
6
投票

你能做到吗?是的。

您可以通过修改 JavaScript 中的原型来扩展任何对象。为了向

Page
对象添加函数,您可以使用
Page
 属性访问 
__proto__
对象的原型。

这是一个将函数

customMethod
添加到所有 Page 对象的简单示例:

const page = await browser.newPage();
page.__proto__.customMethod = async function () {
    // ...
    return 123;
}
console.log(await page.customMethod()); // 123

const anotherPage = await browser.newPage();
console.log(await anotherPage.customMethod()); // 123

注意,您首先需要一个

Page
对象,才能访问原型,因为构造函数(或类)本身并未公开。

你应该这样做吗?没有。

您可能已经注意到上面链接的 MDN 文档上的红色警告。仔细阅读它们。一般来说,不建议更改您正在使用的且不是您自己创建的对象的原型。有人已经创建了原型,他没想到有人会修改它。有关更多信息,请查看此 stackoverflow 问题:

该怎么做?

相反,您应该只使用自己的函数。拥有自己的函数并使用

page
作为参数来调用它们并没有什么问题,如下所示:

// simple function
findAnyByXPath(page);

// your own "namespace" with more functionality
myLibrary.findAnyByXPath(page);
myLibrary.anotherCustomFunction(page);

通常,您也可以扩展类

Page
,但在这种情况下,库不会导出
class
本身。因此,您只能创建一个包装类,它在内部执行相同的功能,但在顶部提供更多功能。但这将是一种非常复杂的方法,在这种情况下确实值得付出努力。


2
投票

扩展@Thomas的answer,如果你想覆盖

Page
的原始方法:

const extendPage = (page: Page) => {
  const { goto: originalGoto } = page;

  page.goto = function goto(url, options) {
    console.log("Goto:", url);
    // do your things
    return originalGoto.apply(page, arguments);
  };

  return page;
};
const page = extendPage(await browser.newPage());

await page.goto("https://google.com"); // Goto: https://www.google.com

要在每次创建新的

Page
时附加其他方法,您可以从
targetcreated
监听
Browser
事件并在回调中扩展页面:

const browser = await puppeteer.launch();

browser.on("targetcreated", async (target: Target) => {
  if (target.type() === "page") {
    const page = await target.page();
    extendPage(page);
  }
});

const page = await browser.newPage(); // extended page

如果您想添加新方法并更新 Typescript 定义:

import { Page, PageEmittedEvents } from "puppeteer";

async function htmlOnly(this: Page) {
  await this.setRequestInterception(true); // enable request interception

  this.on(PageEmittedEvents.Request, (req) => {
    if (req.resourceType() === 'document') return req.continue();
    return req.abort();
  });
}

declare module "puppeteer" {
  interface Page {
    htmlOnly: () => Promise<void>;
  }
}

export const extendPage = (page: Page) => {
  page.htmlOnly = htmlOnly;
  return page;
};
browser.on("targetcreated", async (target: Target) => {
  if (target.type() === "page") {
    const page = await target.page();
    extendPage(page);
  }
});

const page = await browser.newPage();

await page.htmlOnly();

0
投票

“不要每次都翻页”的技巧。只需传递给类实例一次即可。

interface IPageOperator {
  page: Page;
  clickXPath: (selector: string) => Promise<boolean>;
}

class PageOperator implements IPageOperator {
      page: Page;
    
    
      constructor(page: Page) {
        this.page = page;
      }
    
      async clickXPath(selector: string): Promise<boolean> {
        return _clickXPath(this.page, selector);
      }
    }
© www.soinside.com 2019 - 2024. All rights reserved.