使用 Puppeteer 的时候,我们常常要使用 page.evaluate()
或者 page.evaluateHandle()
函数到目标页面的环境里执行一段代码。
如果是简单的代码,比如返回页面标题、改变某个节点的属性,那么直接写在 .evaluate()
里面就行了;但实际生产中,尤其是前厂的 Showman 产品里,要执行的函数往往非常复杂,经常需要组合多个函数:
page.evaluate(() => {
function func1() {}
function func2() {}
// ...
function funcN() {}
func1();
func2();
// ...
funcN();
});
这种场景下,我们必须用上面这种写法,而不是下面这种我们更熟悉的写法:
import func1 from './func1';
import func2 from './func2';
// ...
import funcN from './funcN';
page.evaluate(() => {
func1();
func2();
// ...
funcN();
});
因为被执行的函数会被转换成字符串,传输到目标环境,然后重新实例化成函数,再执行。所以上面这种写法,引擎会在全局环境下查找需要的函数,而那些函数都没传递过去,就会找不到。
如果开发时按照方案一组织代码,会遇到几个问题:
- 子函数放在主函数体内部,不方便独立开发、调试、测试
- 每个主函数内部都需要写死子函数,不方便共享复用
所以我就想从工具链入手,写一个专用工具,可以继续用方案二的形式组织代码,但是编译打包之后,就恢复到方案一的状态。
我选择了 webpack 插件,原因有二:
- 我比较熟悉 webpack
- 这种情况不能用 loader
最后我选择在 compilation.afterProcessAssets
钩子处理 JS。此时 JS 已经打包了所有资源,并且经过 terser 压缩。所以我会先将 bundle 解开(直接用 string.substring
),然后 return
webpack 对象,从中找到目标函数替换。
具体的代码在 GitHub 仓库里,我就不详细解释了(困了),感兴趣的同学可以看看。
欢迎需要在 rpc 环境下执行 JS 的同学使用,欢迎反馈需求和问题。
欢迎吐槽,共同进步