有时我们可能需要在 Puppeteer 环境中执行一段 JS 代码。
根据官方提供的 API,我们有两种选择,一种是添加 script 标签的方式引入 JS。
page.addScriptTag(options)
options
<[Object]>url
<[string]> URL of a script to be added.path
<[string]> Path to the JavaScript file to be injected into frame. Ifpath
is a relative path, then it is resolved relative to [current working directory].content
<[string]> Raw JavaScript content to be injected into frame.type
<[string]> Script type. Use ‘module’ in order to load a Javascript ES6 module. See script for more details.- returns: <[Promise]<[ElementHandle]>> which resolves to the added tag when the script’s onload fires or when the script content was injected into frame.
另一种是使用page.evaluate
1 | const result = await page.evaluate(x => { |
page.addScriptTag
虽然可以引用本地文件作为 JS 执行,但是模块系统(ES6 Module 和 CommonJS 等)支持并不完善,部分 ES6 代码不支持 (最新 Chrome 可忽略)。page.evaluate
中的代码相当于在 DevTools 的控制台执行的,同样也有模块系统和 ES6 的问题,只是函数传值比page.addScriptTag
方便。
所以我认为最好的解决方式是引入 webpack 和 babel 的编译机制。
具体方案是使用 Webpack Node API 配合 memory-fs 将要执行的 JS 文件作为入口,将编译结果输出为字符串,再通过page.evaluate
执行。
首先我们需要使用 webpack、memory-fs 和 babel 模块,所以先安装相关依赖。yarn add webpack memory-fs @babel/core @babel/preset-env babel-loader
接下来我们编写一个buildModule
函数来将指定文件作为入口,将相关模块依赖打包为 JS Bundle 字符串。
1 | const webpack = require('webpack'); |
我们可以在根目录新建babel.config.js
来指定打包时的 babel 配置,当然也可以复用项目已有的配置。
babel.config.js
1 | module.exports = { |
最后,我们准备需要执行的 JS 入口文件,如果需要执行某些函数,可以将相关模块暴露到 window 对象,供 Puppeteer 使用。在这里我们将自己写的 sum 函数暴露到 window 上。
browser_run.js
1 | import add from 'lodash/add'; |
最终在 Puppeteer 中调用buildModule
函数,传入入口文件路径,经由 webpack 打包和 babel 编译,最后通过page.evaluate
函数执行。
1 | const puppeteer = require('puppeteer'); |