前言
目前在开发一个生成骨架屏的 webpack 插件,使用 Puppeteer 来将处理脚本通过 addScriptTag 方法来插入到页面中。
想法是想模仿 webpack 可以通过配置 plugins 来添加自定义的处理规则,问题就出在这里,通过 page.evaluate 来执行页面脚本的时候获取不到我传入的 plugins。
问题
webpack plugin config:
class TestPlugin {
apply(api, ele, options) {
console.log(api, ele, options);
}
}
{
...
port: 8899,
plugins: [
function test(api, ele, options) {
console.log(api, ele, options);
},
new TestPlugin()
],
...
}
使用 puppeteer 的代码:
await page.evaluate(options => {
Skeleton.genSkeleton(options);
}, this.options);
在调用 Skeleton.genSkeleton 方法的时候, options.plugins 变成了 [null, {}]。导致获取不到传递的自定义处理函数。
排查问题
首先确认 webpack 是否接受到参数
wepack 已经接受到了配置的参数
那就只能是 Puppeteer 的问题了
查看了 puppetter 的官方文档。
evaluate 方法接受的参数必须是可以被序列化的。
然后去 github 上查看它的源码发现它使用 JSON.stringify 来进行序列化。
JSON.stringify 的问题了。
进一步分析问题
既然确定是 JSON.stringify 的问题了, 赶紧去看一波 JSON.stringify 的文档。
送上 JSON.stringify() 的MDN地址。
plugins 第二个元素返回 {}, 当函数出现在数组对象中时,在序列化过程中会被转换成 null,所以 plugins 的第一个元素返回 null。
总结
思考一下,为什么有些属性不能被 stringify 序列化。
因为 JSON 是一个通用的文本格式,和语言无关。设想如果将函数定义也 stringify 的话,如何判断是哪种语言,并且通过合适的方式将其呈现出来将会变得特别复杂。特别是和语言相关的一些特性,比如 JavaScript 中的 Symbol。
ECMASCript官方也特意强调了这一点:
It does not attempt to impose ECMAScript's internal data representations on other programming languages. Instead, it shares a small subset of ECMAScript's textual representations with all other programming languages.
有兴趣的小伙伴可以了解一下如何使用 toJSON 来自定义序列化行为。