在Puppeteer的evaluate函数中使用类有什么方法吗?

huangapple go评论62阅读模式
英文:

Is there a way to use a class inside Puppeteer evaluate?

问题

考虑这个非常简单的示例:

class MyClass {
  public add(num: number): number {
    return num + 2;
  }
}
const result = await page.evaluate((NewInstance) => {
  console.log("typeof instance", typeof NewInstance); // undefined
  const d = new NewInstance();
  console.log("result", d.add(10));
  return d.add(10);
}, MyClass);

我已经尝试了我能想到的一切。我希望在这里使用类的主要原因是因为有很多代码,我不想直接包含在evaluate方法内部。这会让代码变得混乱且难以跟踪,所以我想将所有逻辑移动到一个类中,以便更容易理解发生了什么。

这是否可能?

英文:

Consider this really simple example:

class MyClass {
  public add(num: number): number {
    return num + 2;
  }
}
const result = await page.evaluate((NewInstance) => {
  console.log("typeof instance", typeof NewInstance); // undefined
  const d = new NewInstance();
  console.log("result", d.add(10));
  return d.add(10);
}, MyClass);

I've tried everything I could think of. The main reason I want to use a class here, is because there's a LOT of code I don't want to just include inside the evaluate method directly. It's messy and hard to keep track of it, so I wanted to move all logic to a class so it's easier to understand what's going on.

Is this possible?

答案1

得分: 2

这是您要翻译的文本:

"It's possible, but not necessarily great design, depending on what you're trying to do. It's hard to suggest the best solution without knowing the actual use case, so I'll just provide options and let you make the decision.

One approach is to stringify the class (either by hand or with .toString()) or put it in a separate file, then addScriptTag:

const puppeteer = require("puppeteer"); // ^19.6.3

class MyClass {
  add(num) {
    return num + 2;
  }
}

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  await page.goto(
    "https://www.example.com",
    {waitUntil: "domcontentloaded"}
  );
  await page.addScriptTag({content: MyClass.toString()});
  const result = await page.evaluate(() => new MyClass().add(10));
  console.log(result); // => 12
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

See this answer for more examples.

Something like eval is also feasible. If it looks scary, consider that anything you put into a page.evaluate() or page.addScriptTag() is effectively the same thing as far as security goes.

const result = await page.evaluate(MyClassStringified => {
  const MyClass = eval(`(${MyClassStringified})`);
  return new MyClass().add(10);
}, MyClass.toString());

Many other patterns are also possible, like exposing your library via exposeFunction if the logic is Node-based rather than browser-based.

That said, defining the class inside an evaluate may not be as bad as you think:

const addTonsOfCode = () => {

MyClass = class {
  add(num) {
    return num + 2;
  }
}

// ... tons of code ...
};

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  await page.goto(
    "https://www.example.com",
    {waitUntil: "domcontentloaded"}
  );
  await page.evaluate(addTonsOfCode);
  const result = await page.evaluate(() => new MyClass().add(10));
  console.log(result); // => 12
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

I'd prefer to namespace this all into a library:

const addTonsOfCode = () => {

class MyClass {
  add(num) {
    return num + 2;
  }
}

// ... tons of code ...

window.MyLib = {
  MyClass,
  // ...
};

};

Then use with:

await page.evaluate(addTonsOfCode);
await page.evaluate(() => new MyLib.MyClass().add(10));
英文:

It's possible, but not necessarily great design, depending on what you're trying to do. It's hard to suggest the best solution without knowing the actual use case, so I'll just provide options and let you make the decision.

One approach is to stringify the class (either by hand or with .toString()) or put it in a separate file, then addScriptTag:

const puppeteer = require("puppeteer"); // ^19.6.3

class MyClass {
  add(num) {
    return num + 2;
  }
}

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  await page.goto(
    "https://www.example.com",
    {waitUntil: "domcontentloaded"}
  );
  await page.addScriptTag({content: MyClass.toString()});
  const result = await page.evaluate(() => new MyClass().add(10));
  console.log(result); // => 12
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

See this answer for more examples.

Something like eval is also feasible. If it looks scary, consider that anything you put into a page.evaluate() or page.addScriptTag() is effectively the same thing as far as security goes.

const result = await page.evaluate(MyClassStringified => {
  const MyClass = eval(`(${MyClassStringified})`);
  return new MyClass().add(10);
}, MyClass.toString());

Many other patterns are also possible, like exposing your library via exposeFunction if the logic is Node-based rather than browser-based.

That said, defining the class inside an evaluate may not be as bad as you think:

const addTonsOfCode = () => {

MyClass = class {
  add(num) {
    return num + 2;
  }
}

// ... tons of code ...
};

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  await page.goto(
    "https://www.example.com",
    {waitUntil: "domcontentloaded"}
  );
  await page.evaluate(addTonsOfCode);
  const result = await page.evaluate(() => new MyClass().add(10));
  console.log(result); // => 12
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

I'd prefer to namespace this all into a library:

const addTonsOfCode = () => {

class MyClass {
  add(num) {
    return num + 2;
  }
}

// ... tons of code ...

window.MyLib = {
  MyClass,
  // ...
};

};

Then use with:

await page.evaluate(addTonsOfCode);
await page.evaluate(() => new MyLib.MyClass().add(10));

huangapple
  • 本文由 发表于 2023年2月16日 14:52:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75468744.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定